- Campbell Barton's (AKA Ideasman) obj importer script (some split improvements)

- I added support of material and texture import (.mtl files) Textures are assigned to faces and materials too.
This commit is contained in:
Jiri Hnidek 2004-06-14 20:51:09 +00:00
parent f24be4c6ad
commit 317e067ecb

@ -1,176 +1,245 @@
#!BPY #!BPY
""" """
Name: 'Wavefront (.obj)...' Name: 'Wavefront (.obj)...'
Blender: 232 Blender: 232
Group: 'Import' Group: 'Import'
Tooltip: 'Load a Wavefront OBJ File' Tooltip: 'Load a Wavefront OBJ File'
""" """
# $Id$ # $Id$
# #
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# OBJ Import v0.9 by Campbell Barton (AKA Ideasman) # OBJ Import v0.9 by Campbell Barton (AKA Ideasman)
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK ***** # ***** BEGIN GPL LICENSE BLOCK *****
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 # as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, # along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
# ***** END GPL LICENCE BLOCK ***** # ***** END GPL LICENCE BLOCK *****
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
WHITESPACE = [' ', '\n', '\r', '\t', '\f', '\v'] # used for the split function.
NULL_MAT = '(null)' # Name for mesh's that have no mat set.
MATLIMIT = 16 NULL_MAT = '(null)' # Name for mesh's that have no mat set.
#==============================================# MATLIMIT = 16
# 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) ]
#================================================================# DIR = ''
# Replace module deps 'string' for join and split # #
# - Split splits a string into a list, and join does the reverse # #==============================================#
#================================================================# # Return directory, where is file #
def split(splitString, WHITESPACE): #==============================================#
splitList = [] def pathName(path,name):
charIndex = 0 length=len(path)
while charIndex < len(splitString): for CH in range(1, length):
# Skip white space if path[length-CH:] == name:
while charIndex < len(splitString): path = path[:length-CH]
if splitString[charIndex] in WHITESPACE: break
charIndex += 1 return path
else:
break #==============================================#
# 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) ]
#===============================#
# Join list items into a string #
#===============================#
def join(joinList):
joinedString = ""
for listItem in joinList:
joinedString = joinedString + ' ' + str(listItem)
# Remove the first space
joinedString = joinedString[1:]
return joinedString
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 load_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)
for f in mesh.faces:
if mesh.materials[f.mat].name == mat.name:
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):
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(eval(l[1]), eval(l[2]), eval(l[3]))
elif l[0] == 'Kd':
currentMat.setRGBCol(eval(l[1]), eval(l[2]), eval(l[3]))
elif l[0] == 'Ks':
currentMat.setSpecCol(eval(l[1]), eval(l[2]), eval(l[3]))
elif l[0] == 'Ns':
currentMat.setEmit(eval(l[1])/100.0)
elif l[0] == 'd':
currentMat.setAlpha(eval(l[1]))
elif l[0] == 'Tr':
currentMat.setAlpha(eval(l[1]))
elif l[0] == 'map_Ka':
img_fileName = dir + l[1]
load_image(currentMat, img_fileName, 'Ka', mesh)
elif l[0] == 'map_Kd':
img_fileName = dir + l[1]
load_image(currentMat, img_fileName, 'Kd', mesh)
elif l[0] == 'map_Ks':
img_fileName = dir + l[1]
load_image(currentMat, img_fileName, 'Ks', mesh)
lIdx+=1
#==================================================================================#
# This loads data from .obj file #
#==================================================================================#
def load_obj(file):
def applyMat(mesh, f, mat):
# Check weather the 16 mat limit has been met.
if len( mesh.materials ) >= MATLIMIT:
print 'Warning, max material limit reached, using an existing material'
return mesh, f
# Gather text that is not white space and append to splitList mIdx = 0
startWordCharIndex = charIndex for m in mesh.materials:
while charIndex < len(splitString): if m.getName() == mat.getName():
if splitString[charIndex] in WHITESPACE: break break
charIndex += 1 mIdx+=1
# Now we have the first and last chars we can append the word to the list
if charIndex != startWordCharIndex:
splitList.append(splitString[startWordCharIndex:charIndex])
return splitList
#===============================#
# Join list items into a string #
#===============================#
def join(joinList):
joinedString = ""
for listItem in joinList:
joinedString = joinedString + ' ' + str(listItem)
# Remove the first space
joinedString = joinedString[1:]
return joinedString
from Blender import *
def load_obj(file):
# 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)
def applyMat(mesh, f, mat):
# Check weather the 16 mat limit has been met.
if len( mesh.materials ) >= MATLIMIT:
print 'Warning, max material limit reached, using an existing material'
return mesh, f
mIdx = 0 if mIdx == len(mesh.materials):
for m in mesh.materials: mesh.addMaterial(mat)
if m.getName() == mat.getName():
break
mIdx+=1
if mIdx == len(mesh.materials): f.mat = mIdx
mesh.addMaterial(mat) return mesh, 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()
mesh = NMesh.GetRaw() # new empty mesh
objectName = 'mesh' # If we cant get one, use this
uvMapList = [] # store tuple uv pairs here
nullMat = getMat(NULL_MAT)
currentMat = nullMat # Use this mat.
# Main loop
lIdx = 0
while lIdx < len(fileLines):
l = fileLines[lIdx].split()
f.mat = mIdx # Detect a line that will be idnored
return mesh, f if len(l) == 0:
pass
# Get the file name with no path or .obj elif l[0] == '#' or len(l) == 0:
fileName = stripName( stripPath(file) ) pass
# VERTEX
fileLines = open(file, 'r').readlines() elif l[0] == 'v':
# This is a new vert, make a new mesh
mesh = NMesh.GetRaw() # new empty mesh mesh.verts.append( NMesh.Vert(eval(l[1]), eval(l[2]), eval(l[3]) ) )
objectName = 'mesh' # If we cant get one, use this
uvMapList = [] # store tuple uv pairs here
nullMat = getMat(NULL_MAT)
currentMat = nullMat # Use this mat.
# Main loop
lIdx = 0
while lIdx < len(fileLines):
l = split(fileLines[lIdx], WHITESPACE)
# Detect a line that will be idnored
if len(l) == 0:
pass
elif l[0] == '#' or len(l) == 0:
pass
# VERTEX
elif l[0] == 'v':
# This is a new vert, make a new mesh
mesh.verts.append( NMesh.Vert(eval(l[1]), eval(l[2]), eval(l[3]) ) )
elif l[0] == 'vn': elif l[0] == 'vn':
pass pass
elif l[0] == 'vt': elif l[0] == 'vt':
# This is a new vert, make a new mesh # This is a new vert, make a new mesh
uvMapList.append( (eval(l[1]), eval(l[2])) ) uvMapList.append( (eval(l[1]), eval(l[2])) )
elif l[0] == 'f': elif l[0] == 'f':
# Make a face with the correct material. # Make a face with the correct material.
f = NMesh.Face() f = NMesh.Face()
mesh, f = applyMat(mesh, f, currentMat) mesh, f = applyMat(mesh, f, currentMat)
# Set up vIdxLs : Verts # Set up vIdxLs : Verts
# Set up vtIdxLs : UV # Set up vtIdxLs : UV
vIdxLs = [] vIdxLs = []
vtIdxLs = [] vtIdxLs = []
for v in l[1:]: for v in l[1:]:
objVert = split( v, ['/'] ) #objVert = split( v, ['/'] )
objVert = v.split('/', -1)
# VERT INDEX # VERT INDEX
vIdxLs.append(eval(objVert[0]) -1) vIdxLs.append(eval(objVert[0]) -1)
@ -179,8 +248,8 @@ def load_obj(file):
vtIdxLs.append(eval(objVert[0]) -1) # Sticky UV coords vtIdxLs.append(eval(objVert[0]) -1) # Sticky UV coords
else: else:
vtIdxLs.append(eval(objVert[1]) -1) # Seperate UV coords vtIdxLs.append(eval(objVert[1]) -1) # Seperate UV coords
# Quads only, we could import quads using the method below but it polite to import a quad as a quad.f # Quads only, we could import quads using the method below but it polite to import a quad as a quad.f
if len(vIdxLs) == 4: if len(vIdxLs) == 4:
f.v.append(mesh.verts[vIdxLs[0]]) f.v.append(mesh.verts[vIdxLs[0]])
f.v.append(mesh.verts[vIdxLs[1]]) f.v.append(mesh.verts[vIdxLs[1]])
@ -213,7 +282,7 @@ def load_obj(file):
f.uv.append( uvMapList[ vtIdxLs[i+1] ] ) f.uv.append( uvMapList[ vtIdxLs[i+1] ] )
if vtIdxLs[2] < len(uvMapList): if vtIdxLs[2] < len(uvMapList):
f.uv.append( uvMapList[ vtIdxLs[i+2] ] ) f.uv.append( uvMapList[ vtIdxLs[i+2] ] )
mesh.faces.append(f) # move the face onto the mesh mesh.faces.append(f) # move the face onto the mesh
# is o the only vert/face delimeter? # is o the only vert/face delimeter?
@ -224,11 +293,11 @@ def load_obj(file):
NMesh.PutRaw(mesh, fileName + '_' + objectName) NMesh.PutRaw(mesh, fileName + '_' + objectName)
# Make new mesh # Make new mesh
mesh = NMesh.GetRaw() mesh = NMesh.GetRaw()
# New mesh name # New mesh name
objectName = join(l[1:]) # Use join in case of spaces objectName = join(l[1:]) # Use join in case of spaces
# New texture list # New texture list
uvMapList = [] uvMapList = []
elif l[0] == 'usemtl': elif l[0] == 'usemtl':
@ -236,12 +305,19 @@ def load_obj(file):
currentMat = getMat(NULL_MAT) currentMat = getMat(NULL_MAT)
else: else:
currentMat = getMat(join(l[1:])) # Use join in case of spaces currentMat = getMat(join(l[1:])) # Use join in case of spaces
elif l[0] == 'mtllib':
mtl_fileName = l[1]
lIdx+=1 lIdx+=1
# We need to do this to put the last object. # Some material stuff
# All other objects will be put alredy if mtl_fileName != '':
if len(mesh.verts) > 0: load_mtl(DIR, mtl_fileName, mesh)
NMesh.PutRaw(mesh, fileName + '_' + objectName)
# We need to do this to put the last object.
# All other objects will be put alredy
if len(mesh.verts) > 0:
NMesh.PutRaw(mesh, fileName + '_' + objectName)
Window.FileSelector(load_obj, 'Import OBJ') Window.FileSelector(load_obj, 'Import OBJ')