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