* Edited and consistent-ified the File->Import menu item labels and fileselect button...
[blender.git] / release / scripts / ac3d_import.py
1 #!BPY
2
3 """ Registration info for Blender menus:
4 Name: 'AC3D (.ac)...'
5 Blender: 232
6 Group: 'Import'
7 Tip: 'Import an AC3D (.ac) file.'
8 """
9 # $Id$
10 #
11 # --------------------------------------------------------------------------
12 # AC3DImport version 2.32-1 Jan 21, 2004
13 # Program versions: Blender 2.32+ and AC3Db files (means version 0xb)
14 # --------------------------------------------------------------------------
15 # ***** BEGIN GPL LICENSE BLOCK *****
16 #
17 # Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br
18 #
19 # This program is free software; you can redistribute it and/or
20 # modify it under the terms of the GNU General Public License
21 # as published by the Free Software Foundation; either version 2
22 # of the License, or (at your option) any later version.
23 #
24 # This program is distributed in the hope that it will be useful,
25 # but WITHOUT ANY WARRANTY; without even the implied warranty of
26 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27 # GNU General Public License for more details.
28 #
29 # You should have received a copy of the GNU General Public License
30 # along with this program; if not, write to the Free Software Foundation,
31 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
32 #
33 # ***** END GPL LICENCE BLOCK *****
34 # --------------------------------------------------------------------------
35
36 # Note:
37 # Blender doesn't handle n-gons (polygons with more than 4 vertices):
38 #   The script triangulates them, but concave polygons come out wrong and need
39 #   fixing. Avoiding or triangulating concave n-gons in AC3D is a simple way to
40 #   avoid problems.
41
42 # Default folder for AC3D models, change to your liking or leave as "":
43 BASEDIR = ""
44
45 # Set 'GROUP' to 1 to make Blender group imported objects using Empties,
46 # to reproduce the object hierarchy in the .ac file
47 GROUP = 0
48
49 import Blender
50
51 if BASEDIR:
52     BASEDIR = BASEDIR.replace('\\','/')
53     if BASEDIR[-1] != '/': BASEDIR += '/'
54
55 errmsg = ""
56
57 class Obj:
58     
59     def __init__(self, type):
60         self.type = type
61         self.dad = None
62         self.name = ''
63         self.data = ''
64         self.tex = ''
65         self.texrep = [1,1]
66         self.texoff = None
67         self.loc = [0, 0, 0]
68         self.rot = []
69         self.vlist = []
70         self.flist = []
71         self.matlist = []
72         self.kids = 0
73
74 class AC3DImport:
75
76     def __init__(self, filename):
77
78         global errmsg
79
80         print "Trying to import AC3D model(s) from %s ..." % filename
81
82         self.i = 0
83         errmsg = ''
84         self.importdir = Blender.sys.dirname(filename)
85         try:
86             file = open(filename, 'r')
87         except IOError, (errno, strerror):
88             errmsg = "IOError #%s: %s" % (errno, strerror)
89             print errmsg
90             return None
91         header = file.read(5)
92         header, version = header[:4], header[-1]
93         if header != 'AC3D':
94             file.close()
95             errmsg = 'Invalid file -- AC3D header not found.'
96             print errmsg
97             return None
98         elif version != 'b':
99             print 'AC3D file version 0x%s.' % version
100             print 'This importer is for version 0xb, so it may fail.' 
101
102         self.token = {'OBJECT':     self.parse_obj,
103                       'numvert':    self.parse_vert,
104                       'numsurf':    self.parse_surf,
105                       'name':       self.parse_name,
106                       'data':       self.parse_data,
107                       'kids':       self.parse_kids,
108                       'loc':        self.parse_loc,
109                       'rot':        self.parse_rot,
110                       'MATERIAL':   self.parse_mat,
111                       'texture':    self.parse_tex,
112                       'texrep':     self.parse_texrep,
113                       'texoff':     self.parse_texoff}
114
115         self.objlist = []
116         self.mlist = []
117         self.dads = []
118         self.kids = []
119         self.dad = None
120
121         self.lines = file.readlines()
122         self.lines.append('')
123         self.parse_file()
124         file.close()
125         
126         self.testAC3DImport()
127                 
128     def parse_obj(self, value):
129         if self.kids:
130             while not self.kids[-1]:
131                 self.kids.pop()
132                 self.dad = self.dad.dad
133             self.kids[-1] -= 1
134         new = Obj(value)
135         new.dad = self.dad
136         new.name = value
137         self.objlist.append(new)
138
139     def parse_kids(self, value):
140         kids = int(value)
141         if kids:
142             self.kids.append(kids)
143             self.dad = self.objlist[-1]
144         self.objlist[-1].kids = kids
145
146     def parse_name(self, value):
147         name = value.split('"')[1]
148         self.objlist[-1].name = name
149
150     def parse_data(self, value):
151         data = self.lines[self.i].strip()
152         self.objlist[-1].data = data
153
154     def parse_tex(self, value):
155         texture = value.split('"')[1]
156         self.objlist[-1].tex = texture
157
158     def parse_texrep(self, trash):
159         trep = self.lines[self.i - 1]
160         trep = trep.split()
161         trep = [float(trep[1]), float(trep[2])]
162         self.objlist[-1].texrep = trep
163         self.objlist[-1].texoff = [0, 0]
164
165     def parse_texoff(self, trash):
166         toff = self.lines[self.i - 1]
167         toff = toff.split()
168         toff = [float(toff[1]), float(toff[2])]
169         self.objlist[-1].texoff = toff
170         
171     def parse_mat(self, value):
172         i = self.i - 1
173         lines = self.lines
174         line = lines[i].split()
175         mat_name = ''
176         mat_col = mat_spec_col = [0,0,0]
177         mat_alpha = 1
178
179         while line[0] == 'MATERIAL':
180             mat_name = line[1].split('"')[1]
181             mat_col = map(float,[line[3],line[4],line[5]])
182             mat_spec_col = map(float,[line[15],line[16],line[17]])
183             mat_alpha = float(line[-1])
184             mat_alpha = 1 - mat_alpha
185             self.mlist.append([mat_name, mat_col, mat_spec_col, mat_alpha])
186             i += 1
187             line = lines[i].split()
188
189         self.i = i
190
191     def parse_rot(self, trash):
192         i = self.i - 1
193         rot = self.lines[i].split(' ', 1)[1]
194         rot = map(float, rot.split())
195         self.objlist[-1].rot = rot
196
197     def parse_loc(self, trash):
198         i = self.i - 1
199         loc = self.lines[i].split(' ', 1)[1]
200         loc = map(float, loc.split())
201         self.objlist[-1].loc = loc
202
203     def parse_vert(self, value):
204         i = self.i
205         lines = self.lines
206         obj = self.objlist[-1]
207         vlist = obj.vlist
208         n = int(value)
209
210         while n:
211             line = lines[i].split()
212             line = map(float, line)
213             vlist.append(line)
214             n -= 1
215             i += 1
216
217         self.i = i
218
219         rot = obj.rot
220         if rot:
221             nv = len(vlist)
222             for j in range(nv):
223                 v = vlist[j]
224                 t = [0,0,0]
225                 t[0] = rot[0]*v[0] + rot[3]*v[1] + rot[6]*v[2]
226                 t[1] = rot[1]*v[0] + rot[4]*v[1] + rot[7]*v[2]
227                 t[2] = rot[2]*v[0] + rot[5]*v[1] + rot[8]*v[2]
228                 vlist[j] = t
229
230         loc = obj.loc
231         dad = obj.dad
232         while dad:
233             for j in [0, 1, 2]:
234                 loc[j] += dad.loc[j]
235             dad = dad.dad
236
237         for v in vlist:
238             for j in [0, 1, 2]:
239                 v[j] += loc[j]
240
241     def parse_surf(self, value):
242         i = self.i
243         is_smooth = 0
244         double_sided = 0
245         lines = self.lines
246         obj = self.objlist[-1]
247         matlist = obj.matlist
248         numsurf = int(value)
249
250         while numsurf:
251             flags = lines[i].split()
252             flaglow = 0
253             if len(flags[1]) > 3: flaglow = int(flags[1][3])
254             flaghigh = int(flags[1][2])
255             is_smooth = flaghigh & 1
256             twoside = flaghigh & 2
257             mat = lines[i+1].split()
258             mat = int(mat[1])
259             if not mat in matlist: matlist.append(mat)
260             refs = lines[i+2].split()
261             refs = int(refs[1])
262             i += 3
263             face = []
264             faces = []
265             fuv = []
266             rfs = refs
267
268             while rfs:
269                 line = lines[i].split()
270                 v = int(line[0])
271                 uv = [float(line[1]), float(line[2])]
272                 face.append([v, uv])
273                 rfs -= 1
274                 i += 1
275                 
276             if flaglow:
277                 while len(face) >= 2:
278                     cut = face[:2]
279                     faces.append(cut)
280                     face = face[1:]
281
282                 if flaglow == 1:
283                     face = [faces[-1][-1], faces[0][0]]
284                     faces.append(face)
285
286             else:
287                 while len(face) > 4:
288                     cut = face[:4]
289                     face = face[3:]
290                     face.insert(0, cut[0])
291                     faces.append(cut)        
292
293                 faces.append(face)
294
295             for f in faces:
296                 f.append(mat)
297                 f.append(is_smooth)
298                 f.append(twoside)
299                 self.objlist[-1].flist.append(f)
300
301             numsurf -= 1      
302
303                             
304         self.i = i
305
306     def parse_file(self):
307         i = 1
308         lines = self.lines
309         line = lines[i].split()
310
311         while line:
312             kw = ''
313             for k in self.token.keys():
314                 if line[0] == k:
315                     kw = k
316                     break
317             i += 1
318             if kw:
319                 self.i = i
320                 self.token[kw](line[1])
321                 i = self.i
322             line = lines[i].split()
323
324     def testAC3DImport(self):
325         global GROUP
326         scene = Blender.Scene.GetCurrent()
327
328         bmat = []
329         for mat in self.mlist:
330             name = mat[0]
331             m = Blender.Material.New(name)
332             m.rgbCol = (mat[1][0], mat[1][1], mat[1][2])
333             m.specCol = (mat[2][0], mat[2][1], mat[2][2])
334             m.alpha = mat[3]
335             bmat.append(m)
336
337         for obj in self.objlist:
338             if obj.type == 'world':
339                 continue
340             elif obj.type == 'group':
341                 if not GROUP: continue
342                 empty = Blender.Object.New('Empty')
343                 empty.name = obj.name
344                 scene.link(empty)
345                 if self.dads:
346                     dadobj = Blender.Object.get(self.dads.pop())
347                     dadobj.makeParent([empty])
348                 while obj.kids:
349                     self.dads.append(empty.name)
350                     obj.kids -= 1
351                 continue
352             mesh = Blender.NMesh.New()
353             if (obj.data): mesh.name = obj.data
354             mesh.hasFaceUV(1)
355
356             tex = None
357             if obj.tex != '':
358                 try:
359                     tex = Blender.Image.Load(obj.tex)
360                     # Commented because it's unnecessary:
361                     #tex.xrep = int(obj.texrep[0])
362                     #tex.yrep = int(obj.texrep[1])
363                 except:
364                     try:
365                         obj.tex = self.importdir + '/' + obj.tex
366                         tex = Blender.Image.Load(obj.tex)
367                     except:
368                         print "Couldn't load texture: %s" % obj.tex
369
370             for v in obj.vlist:
371                 bvert = Blender.NMesh.Vert(v[0],v[1],v[2])
372                 mesh.verts.append(bvert)
373
374             objmat_indices = []
375             for mat in bmat:
376                 if bmat.index(mat) in obj.matlist:
377                     objmat_indices.append(bmat.index(mat))
378                     mesh.materials.append(mat)
379             for f in obj.flist:
380                 twoside = f[-1]
381                 is_smooth = f[-2]
382                 fmat = f[-3]
383                 f=f[:-3]
384                 bface = Blender.NMesh.Face()
385                 bface.smooth = is_smooth
386                 if twoside: bface.mode |= Blender.NMesh.FaceModes['TWOSIDE']
387                 if tex:
388                     bface.mode |= Blender.NMesh.FaceModes['TEX']
389                     bface.image = tex
390                 bface.materialIndex = objmat_indices.index(fmat)
391                 if obj.texoff:
392                     uoff = obj.texoff[0]
393                     voff = obj.texoff[1]
394                     urep = obj.texrep[0]
395                     vrep = obj.texrep[1]
396                     for vi in range(len(f)):
397                         f[vi][1][0] *= urep
398                         f[vi][1][1] *= vrep
399                         f[vi][1][0] += uoff
400                         f[vi][1][1] += voff
401
402                 for vi in range(len(f)):
403                     bface.v.append(mesh.verts[f[vi][0]])
404                     bface.uv.append((f[vi][1][0], f[vi][1][1]))
405                 mesh.faces.append(bface)
406
407             mesh.mode = 0
408             object = Blender.NMesh.PutRaw(mesh)
409             object.setName(obj.name)
410             object.setEuler([1.5707963,0,0]) # align ac3d w/ Blender
411             if self.dads:
412                 dadobj = Blender.Object.get(self.dads.pop())
413                 dadobj.makeParent([object])
414
415         print '...done!'
416
417 # End of class AC3DImport
418
419 def filesel_callback(filename):
420   test = AC3DImport(filename)
421
422 Blender.Window.FileSelector(filesel_callback, "Import AC3D")