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