forked from bartvdbraak/blender
d34f89b755
* Added items for importing DXF, VRML, etc. in the File->Import menu, that just call the normal Open function. Most people don't even know that you can open these formats through the normal Open fileselect, so this will make it more obvious. * Removed the 'Export Selected' menu, and put poor old lonely STL in the Import and Export menus too. Most of the exporters export only the selected object anyway, so it's not really a necessary distinction to make.
248 lines
8.2 KiB
Python
248 lines
8.2 KiB
Python
#!BPY
|
|
|
|
"""
|
|
Name: 'Wavefront (.obj)...'
|
|
Blender: 232
|
|
Group: 'Import'
|
|
Tooltip: 'Load a Wavefront OBJ File'
|
|
"""
|
|
|
|
# $Id$
|
|
#
|
|
# --------------------------------------------------------------------------
|
|
# 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 *****
|
|
# --------------------------------------------------------------------------
|
|
|
|
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
|
|
|
|
#==============================================#
|
|
# 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) ]
|
|
|
|
#================================================================#
|
|
# Replace module deps 'string' for join and split # #
|
|
# - Split splits a string into a list, and join does the reverse #
|
|
#================================================================#
|
|
def split(splitString, WHITESPACE):
|
|
splitList = []
|
|
charIndex = 0
|
|
while charIndex < len(splitString):
|
|
# Skip white space
|
|
while charIndex < len(splitString):
|
|
if splitString[charIndex] in WHITESPACE:
|
|
charIndex += 1
|
|
else:
|
|
break
|
|
|
|
# Gather text that is not white space and append to splitList
|
|
startWordCharIndex = charIndex
|
|
while charIndex < len(splitString):
|
|
if splitString[charIndex] in WHITESPACE: break
|
|
charIndex += 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
|
|
for m in mesh.materials:
|
|
if m.getName() == mat.getName():
|
|
break
|
|
mIdx+=1
|
|
|
|
if mIdx == len(mesh.materials):
|
|
mesh.addMaterial(mat)
|
|
|
|
f.mat = mIdx
|
|
return mesh, f
|
|
|
|
# Get the file name with no path or .obj
|
|
fileName = stripName( 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 = 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':
|
|
pass
|
|
|
|
elif l[0] == 'vt':
|
|
# This is a new vert, make a new mesh
|
|
uvMapList.append( (eval(l[1]), eval(l[2])) )
|
|
|
|
elif l[0] == 'f':
|
|
|
|
# Make a face with the correct material.
|
|
f = NMesh.Face()
|
|
mesh, f = applyMat(mesh, f, currentMat)
|
|
|
|
# Set up vIdxLs : Verts
|
|
# Set up vtIdxLs : UV
|
|
vIdxLs = []
|
|
vtIdxLs = []
|
|
for v in l[1:]:
|
|
objVert = split( v, ['/'] )
|
|
|
|
# VERT INDEX
|
|
vIdxLs.append(eval(objVert[0]) -1)
|
|
# UV
|
|
if len(objVert) == 1:
|
|
vtIdxLs.append(eval(objVert[0]) -1) # Sticky UV coords
|
|
else:
|
|
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
|
|
if len(vIdxLs) == 4:
|
|
f.v.append(mesh.verts[vIdxLs[0]])
|
|
f.v.append(mesh.verts[vIdxLs[1]])
|
|
f.v.append(mesh.verts[vIdxLs[2]])
|
|
f.v.append(mesh.verts[vIdxLs[3]])
|
|
# UV MAPPING
|
|
if uvMapList:
|
|
if vtIdxLs[0] < len(uvMapList):
|
|
f.uv.append( uvMapList[ vtIdxLs[0] ] )
|
|
if vtIdxLs[1] < len(uvMapList):
|
|
f.uv.append( uvMapList[ vtIdxLs[1] ] )
|
|
if vtIdxLs[2] < len(uvMapList):
|
|
f.uv.append( uvMapList[ vtIdxLs[2] ] )
|
|
if vtIdxLs[3] < len(uvMapList):
|
|
f.uv.append( uvMapList[ vtIdxLs[3] ] )
|
|
mesh.faces.append(f) # move the face onto the mesh
|
|
|
|
elif len(vIdxLs) >= 3: # This handles tri's and fans
|
|
for i in range(len(vIdxLs)-2):
|
|
f = NMesh.Face()
|
|
mesh, f = applyMat(mesh, f, currentMat)
|
|
f.v.append(mesh.verts[vIdxLs[0]])
|
|
f.v.append(mesh.verts[vIdxLs[i+1]])
|
|
f.v.append(mesh.verts[vIdxLs[i+2]])
|
|
# UV MAPPING
|
|
if uvMapList:
|
|
if vtIdxLs[0] < len(uvMapList):
|
|
f.uv.append( uvMapList[ vtIdxLs[0] ] )
|
|
if vtIdxLs[1] < len(uvMapList):
|
|
f.uv.append( uvMapList[ vtIdxLs[i+1] ] )
|
|
if vtIdxLs[2] < len(uvMapList):
|
|
f.uv.append( uvMapList[ vtIdxLs[i+2] ] )
|
|
|
|
mesh.faces.append(f) # move the face onto the mesh
|
|
|
|
# is o the only vert/face delimeter?
|
|
# if not we could be screwed.
|
|
elif l[0] == 'o':
|
|
# Make sure the objects is worth puttong
|
|
if len(mesh.verts) > 0:
|
|
NMesh.PutRaw(mesh, fileName + '_' + objectName)
|
|
# Make new mesh
|
|
mesh = NMesh.GetRaw()
|
|
|
|
# New mesh name
|
|
objectName = join(l[1:]) # Use join in case of spaces
|
|
|
|
# New texture list
|
|
uvMapList = []
|
|
|
|
elif l[0] == 'usemtl':
|
|
if l[1] == '(null)':
|
|
currentMat = getMat(NULL_MAT)
|
|
else:
|
|
currentMat = getMat(join(l[1:])) # Use join in case of spaces
|
|
|
|
lIdx+=1
|
|
|
|
# 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')
|