364fbc590befe47d099c18edb10e74d5003ab005
[blender.git] / release / scripts / obj_import.py
1 #!BPY 
2  
3 """ 
4 Name: 'Wavefront (*.obj)' 
5 Blender: 232 
6 Group: 'Import' 
7 Tooltip: 'Load a Wavefront OBJ File' 
8  """ 
9
10 # $Id$
11 #
12 # -------------------------------------------------------------------------- 
13 # OBJ Import v0.9 by Campbell Barton (AKA Ideasman) 
14 # -------------------------------------------------------------------------- 
15 # ***** BEGIN GPL LICENSE BLOCK ***** 
16
17 # This program is free software; you can redistribute it and/or 
18 # modify it under the terms of the GNU General Public License 
19 # as published by the Free Software Foundation; either version 2 
20 # of the License, or (at your option) any later version. 
21
22 # This program is distributed in the hope that it will be useful, 
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of 
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
25 # GNU General Public License for more details. 
26
27 # You should have received a copy of the GNU General Public License 
28 # along with this program; if not, write to the Free Software Foundation, 
29 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
30
31 # ***** END GPL LICENCE BLOCK ***** 
32 # -------------------------------------------------------------------------- 
33  
34 WHITESPACE = [' ', '\n', '\r', '\t', '\f', '\v'] # used for the split function. 
35 NULL_MAT = '(null)' # Name for mesh's that have no mat set. 
36
37 MATLIMIT = 16 
38
39 #==============================================# 
40 # Strips the slashes from the back of a string # 
41 #==============================================# 
42 def stripPath(path): 
43    for CH in range(len(path), 0, -1): 
44       if path[CH-1] == "/" or path[CH-1] == "\\": 
45          path = path[CH:] 
46          break 
47    return path 
48     
49 #====================================================# 
50 # Strips the prefix off the name before writing      # 
51 #====================================================# 
52 def stripName(name): # name is a string 
53    prefixDelimiter = '.' 
54    return name[ : name.find(prefixDelimiter) ] 
55
56 #================================================================# 
57 # Replace module deps 'string' for join and split #              # 
58 # - Split splits a string into a list, and join does the reverse # 
59 #================================================================# 
60 def split(splitString, WHITESPACE): 
61    splitList = [] 
62    charIndex = 0 
63    while charIndex < len(splitString): 
64       # Skip white space 
65       while charIndex < len(splitString): 
66          if splitString[charIndex] in WHITESPACE:          
67             charIndex += 1 
68          else: 
69             break 
70        
71       # Gather text that is not white space and append to splitList 
72       startWordCharIndex = charIndex 
73       while charIndex < len(splitString): 
74          if splitString[charIndex] in  WHITESPACE: break 
75          charIndex += 1 
76           
77       # Now we have the first and last chars we can append the word to the list 
78       if charIndex != startWordCharIndex: 
79          splitList.append(splitString[startWordCharIndex:charIndex]) 
80              
81    return splitList 
82
83 #===============================# 
84 # Join list items into a string # 
85 #===============================# 
86 def join(joinList): 
87    joinedString = "" 
88    for listItem in joinList: 
89       joinedString = joinedString + ' ' + str(listItem) 
90
91    # Remove the first space 
92    joinedString = joinedString[1:] 
93    return joinedString 
94
95
96 from Blender import * 
97
98 def load_obj(file): 
99    # This gets a mat or creates one of the requested name if none exist. 
100    def getMat(matName): 
101       # Make a new mat 
102       try: 
103          return Material.Get(matName) 
104       except: 
105          return Material.New(matName) 
106     
107    def applyMat(mesh, f, mat): 
108       # Check weather the 16 mat limit has been met. 
109       if len( mesh.materials ) >= MATLIMIT: 
110          print 'Warning, max material limit reached, using an existing material' 
111          return mesh, f 
112        
113       mIdx = 0 
114       for m in mesh.materials: 
115          if m.getName() == mat.getName(): 
116             break 
117          mIdx+=1 
118        
119       if mIdx == len(mesh.materials): 
120         mesh.addMaterial(mat) 
121        
122       f.mat = mIdx 
123       return mesh, f 
124     
125    # Get the file name with no path or .obj 
126    fileName = stripName( stripPath(file) ) 
127     
128    fileLines = open(file, 'r').readlines() 
129     
130    mesh = NMesh.GetRaw() # new empty mesh 
131     
132    objectName = 'mesh' # If we cant get one, use this 
133     
134    uvMapList = [] # store tuple uv pairs here 
135     
136    nullMat = getMat(NULL_MAT) 
137     
138    currentMat = nullMat # Use this mat. 
139     
140    # Main loop 
141    lIdx = 0 
142    while lIdx < len(fileLines): 
143       l = split(fileLines[lIdx], WHITESPACE) 
144        
145       # Detect a line that will be idnored 
146       if len(l) == 0: 
147          pass 
148       elif l[0] == '#' or len(l) == 0: 
149          pass 
150       # VERTEX 
151       elif l[0] == 'v': 
152          # This is a new vert, make a new mesh 
153          mesh.verts.append( NMesh.Vert(eval(l[1]), eval(l[2]), eval(l[3]) ) ) 
154           
155       elif l[0] == 'vn':
156          pass
157           
158       elif l[0] == 'vt':
159          # This is a new vert, make a new mesh
160          uvMapList.append( (eval(l[1]), eval(l[2])) )
161           
162       elif l[0] == 'f': 
163           
164          # Make a face with the correct material. 
165          f = NMesh.Face() 
166          mesh, f = applyMat(mesh, f, currentMat) 
167           
168          # Set up vIdxLs : Verts 
169          # Set up vtIdxLs : UV 
170          vIdxLs = []
171          vtIdxLs = []
172          for v in l[1:]: 
173             objVert = split( v, ['/'] )
174              
175             # VERT INDEX
176             vIdxLs.append(eval(objVert[0]) -1)
177             # UV
178             if len(objVert) == 1:
179                vtIdxLs.append(eval(objVert[0]) -1) # Sticky UV coords
180             else:
181                vtIdxLs.append(eval(objVert[1]) -1) # Seperate UV coords
182           
183          # Quads only, we could import quads using the method below but it polite to import a quad as a quad.f 
184          if len(vIdxLs) == 4:
185             f.v.append(mesh.verts[vIdxLs[0]])
186             f.v.append(mesh.verts[vIdxLs[1]])
187             f.v.append(mesh.verts[vIdxLs[2]])
188             f.v.append(mesh.verts[vIdxLs[3]])
189             # UV MAPPING
190             if uvMapList:
191                if vtIdxLs[0] < len(uvMapList):
192                   f.uv.append( uvMapList[ vtIdxLs[0] ] )
193                if vtIdxLs[1] < len(uvMapList):
194                   f.uv.append( uvMapList[ vtIdxLs[1] ] )
195                if vtIdxLs[2] < len(uvMapList):
196                   f.uv.append( uvMapList[ vtIdxLs[2] ] )
197                if vtIdxLs[3] < len(uvMapList):
198                   f.uv.append( uvMapList[ vtIdxLs[3] ] )
199             mesh.faces.append(f) # move the face onto the mesh
200
201          elif len(vIdxLs) >= 3: # This handles tri's and fans
202             for i in range(len(vIdxLs)-2):
203                f = NMesh.Face()
204                mesh, f = applyMat(mesh, f, currentMat)
205                f.v.append(mesh.verts[vIdxLs[0]])
206                f.v.append(mesh.verts[vIdxLs[i+1]])
207                f.v.append(mesh.verts[vIdxLs[i+2]])
208                # UV MAPPING
209                if uvMapList:
210                   if vtIdxLs[0] < len(uvMapList):
211                      f.uv.append( uvMapList[ vtIdxLs[0] ] )
212                   if vtIdxLs[1] < len(uvMapList):
213                      f.uv.append( uvMapList[ vtIdxLs[i+1] ] )
214                   if vtIdxLs[2] < len(uvMapList):
215                      f.uv.append( uvMapList[ vtIdxLs[i+2] ] )
216                 
217                mesh.faces.append(f) # move the face onto the mesh
218        
219       # is o the only vert/face delimeter?
220       # if not we could be screwed.
221       elif l[0] == 'o':
222          # Make sure the objects is worth puttong
223          if len(mesh.verts) > 0:
224             NMesh.PutRaw(mesh, fileName + '_' + objectName)
225          # Make new mesh
226          mesh = NMesh.GetRaw()
227           
228          # New mesh name
229          objectName = join(l[1:]) # Use join in case of spaces
230           
231          # New texture list 
232          uvMapList = []
233        
234       elif l[0] == 'usemtl':
235          if l[1] == '(null)':
236             currentMat = getMat(NULL_MAT)
237          else:
238             currentMat = getMat(join(l[1:])) # Use join in case of spaces
239              
240       lIdx+=1 
241
242    # We need to do this to put the last object. 
243    # All other objects will be put alredy 
244    if len(mesh.verts) > 0: 
245       NMesh.PutRaw(mesh, fileName + '_' + objectName) 
246
247 Window.FileSelector(load_obj, 'SELECT OBJ FILE')