Integrated Freestyle to rendering pipeline
[blender.git] / release / scripts / ac3d_import.py
1 #!BPY
2
3 """ Registration info for Blender menus:
4 Name: 'AC3D (.ac)...'
5 Blender: 243
6 Group: 'Import'
7 Tip: 'Import an AC3D (.ac) file.'
8 """
9
10 __author__ = "Willian P. Germano"
11 __url__ = ("blender", "blenderartists.org", "AC3D's homepage, http://www.ac3d.org",
12         "PLib 3d gaming lib, http://plib.sf.net")
13 __version__ = "2.48.1 2009-01-11"
14
15 __bpydoc__ = """\
16 This script imports AC3D models into Blender.
17
18 AC3D is a simple and affordable commercial 3d modeller also built with OpenGL.
19 The .ac file format is an easy to parse text format well supported,
20 for example, by the PLib 3d gaming library.
21
22 Supported:<br>
23     UV-textured meshes with hierarchy (grouping) information.
24
25 Missing:<br>
26     The url tag is irrelevant for Blender.
27
28 Known issues:<br>
29     - Some objects may be imported with wrong normals due to wrong information in the model itself. This can be noticed by strange shading, like darker than expected parts in the model. To fix this, select the mesh with wrong normals, enter edit mode and tell Blender to recalculate the normals, either to make them point outside (the usual case) or inside.<br>
30  
31 Config Options:<br>
32     - display transp (toggle): if "on", objects that have materials with alpha < 1.0 are shown with translucency (transparency) in the 3D View.<br>
33     - subdiv (toggle): if "on", ac3d objects meant to be subdivided receive a SUBSURF modifier in Blender.<br>
34     - emis as mircol: store the emissive rgb color from AC3D as mirror color in Blender -- this is a hack to preserve the values and be able to export them using the equivalent option in the exporter.<br>
35     - textures dir (string): if non blank, when imported texture paths are
36 wrong in the .ac file, Blender will also look for them at this dir.
37
38 Notes:<br>
39    - When looking for assigned textures, Blender tries in order: the actual
40 paths from the .ac file, the .ac file's dir and the default textures dir path
41 users can configure (see config options above).
42 """
43
44 # $Id$
45 #
46 # --------------------------------------------------------------------------
47 # AC3DImport version 2.43.1 Feb 21, 2007
48 # Program versions: Blender 2.43 and AC3Db files (means version 0xb)
49 # changed: better triangulation of ngons, more fixes to support bad .ac files,
50 # option to display transp mats in 3d view, support "subdiv" tag (via SUBSURF modifier)
51 # --------------------------------------------------------------------------
52 # Thanks: Melchior Franz for extensive bug testing and reporting, making this
53 # version cope much better with old or bad .ac files, among other improvements;
54 # Stewart Andreason for reporting a serious crash; Francesco Brisa for the
55 # emis as mircol functionality (w/ patch).
56 # --------------------------------------------------------------------------
57 # ***** BEGIN GPL LICENSE BLOCK *****
58 #
59 # Copyright (C) 2004-2009: Willian P. Germano, wgermano _at_ ig.com.br
60 #
61 # This program is free software; you can redistribute it and/or
62 # modify it under the terms of the GNU General Public License
63 # as published by the Free Software Foundation; either version 2
64 # of the License, or (at your option) any later version.
65 #
66 # This program is distributed in the hope that it will be useful,
67 # but WITHOUT ANY WARRANTY; without even the implied warranty of
68 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
69 # GNU General Public License for more details.
70 #
71 # You should have received a copy of the GNU General Public License
72 # along with this program; if not, write to the Free Software Foundation,
73 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
74 #
75 # ***** END GPL LICENCE BLOCK *****
76 # --------------------------------------------------------------------------
77
78 from math import radians
79
80 import Blender
81 from Blender import Scene, Object, Mesh, Lamp, Registry, sys as bsys, Window, Image, Material, Modifier
82 from Blender.sys import dirsep
83 from Blender.Mathutils import Vector, Matrix, Euler
84 from Blender.Geometry import PolyFill
85
86 # Default folder for AC3D textures, to override wrong paths, change to your
87 # liking or leave as "":
88 TEXTURES_DIR = ""
89
90 DISPLAY_TRANSP = True
91
92 SUBDIV = True
93
94 EMIS_AS_MIRCOL = False
95
96
97 tooltips = {
98         'DISPLAY_TRANSP': 'Turn transparency on in the 3d View for objects using materials with alpha < 1.0.',
99         'SUBDIV': 'Apply a SUBSURF modifier to objects meant to appear subdivided.',
100         'TEXTURES_DIR': 'Additional folder to look for missing textures.',
101         'EMIS_AS_MIRCOL': 'Store emis color as mirror color in Blender.'        
102 }
103
104 def update_registry():
105         global TEXTURES_DIR, DISPLAY_TRANSP, EMIS_AS_MIRCOL
106         rd = dict([('tooltips', tooltips), ('TEXTURES_DIR', TEXTURES_DIR), ('DISPLAY_TRANSP', DISPLAY_TRANSP), ('SUBDIV', SUBDIV), ('EMIS_AS_MIRCOL', EMIS_AS_MIRCOL)])
107         Registry.SetKey('ac3d_import', rd, True)
108
109 rd = Registry.GetKey('ac3d_import', True)
110
111 if rd:
112         if 'GROUP' in rd:
113                 update_registry()
114         try:
115                 TEXTURES_DIR = rd['TEXTURES_DIR']
116                 DISPLAY_TRANSP = rd['DISPLAY_TRANSP']
117                 SUBDIV = rd['SUBDIV']
118                 EMIS_AS_MIRCOL = rd['EMIS_AS_MIRCOL']
119         except:
120                 update_registry()
121 else: update_registry()
122
123 if TEXTURES_DIR:
124         oldtexdir = TEXTURES_DIR
125         if dirsep == '/': TEXTURES_DIR = TEXTURES_DIR.replace('\\', '/')
126         if TEXTURES_DIR[-1] != dirsep: TEXTURES_DIR = "%s%s" % (TEXTURES_DIR, dirsep)
127         if oldtexdir != TEXTURES_DIR: update_registry()
128
129
130 VERBOSE = True
131 rd = Registry.GetKey('General', True)
132 if rd:
133         if rd.has_key('verbose'):
134                 VERBOSE = rd['verbose']
135
136         
137 errmsg = ""
138
139 # Matrix to align ac3d's coordinate system with Blender's one,
140 # it's a -90 degrees rotation around the x axis:
141 AC_TO_BLEND_MATRIX = Matrix([1, 0, 0], [0, 0, 1], [0, -1, 0])
142
143 AC_WORLD = 0
144 AC_GROUP = 1
145 AC_POLY = 2
146 AC_LIGHT = 3
147 AC_OB_TYPES = {
148         'world': AC_WORLD,
149         'group': AC_GROUP,
150         'poly':  AC_POLY,
151         'light':  AC_LIGHT
152         }
153
154 AC_OB_BAD_TYPES_LIST = [] # to hold references to unknown (wrong) ob types
155
156 def inform(msg):
157         global VERBOSE
158         if VERBOSE: print msg
159
160 def euler_in_radians(eul):
161         "Used while there's a bug in the BPY API"
162         eul.x = radians(eul.x)
163         eul.y = radians(eul.y)
164         eul.z = radians(eul.z)
165         return eul
166
167 class Obj:
168         
169         def __init__(self, type):
170                 self.type = type
171                 self.dad = None
172                 self.name = ''
173                 self.data = ''
174                 self.tex = ''
175                 self.texrep = [1,1]
176                 self.texoff = None
177                 self.loc = []
178                 self.rot = []
179                 self.size = []
180                 self.crease = 30
181                 self.subdiv = 0
182                 self.vlist = []
183                 self.flist_cfg = []
184                 self.flist_v = []
185                 self.flist_uv = []
186                 self.elist = []
187                 self.matlist = []
188                 self.kids = 0
189
190                 self.bl_obj = None # the actual Blender object created from this data
191
192 class AC3DImport:
193
194         def __init__(self, filename):
195
196                 global errmsg
197
198                 self.scene = Scene.GetCurrent()
199
200                 self.i = 0
201                 errmsg = ''
202                 self.importdir = bsys.dirname(filename)
203                 try:
204                         file = open(filename, 'r')
205                 except IOError, (errno, strerror):
206                         errmsg = "IOError #%s: %s" % (errno, strerror)
207                         Blender.Draw.PupMenu('ERROR: %s' % errmsg)
208                         inform(errmsg)
209                         return None
210                 header = file.read(5)
211                 header, version = header[:4], header[-1]
212                 if header != 'AC3D':
213                         file.close()
214                         errmsg = 'AC3D header not found (invalid file)'
215                         Blender.Draw.PupMenu('ERROR: %s' % errmsg)
216                         inform(errmsg)
217                         return None
218                 elif version != 'b':
219                         inform('AC3D file version 0x%s.' % version)
220                         inform('This importer is for version 0xb, so it may fail.')
221
222                 self.token = {'OBJECT':         self.parse_obj,
223                                           'numvert':    self.parse_vert,
224                                           'numsurf':    self.parse_surf,
225                                           'name':               self.parse_name,
226                                           'data':               self.parse_data,
227                                           'kids':               self.parse_kids,
228                                           'loc':                self.parse_loc,
229                                           'rot':                self.parse_rot,
230                                           'MATERIAL':   self.parse_mat,
231                                           'texture':    self.parse_tex,
232                                           'texrep':             self.parse_texrep,
233                                           'texoff':             self.parse_texoff,
234                                           'subdiv':             self.parse_subdiv,
235                                           'crease':             self.parse_crease}
236
237                 self.objlist = []
238                 self.mlist = []
239                 self.kidsnumlist = []
240                 self.dad = None
241
242                 self.lines = file.readlines()
243                 self.lines.append('')
244                 self.parse_file()
245                 file.close()
246                 
247                 self.testAC3DImport()
248                                 
249         def parse_obj(self, value):
250                 kidsnumlist = self.kidsnumlist
251                 if kidsnumlist:
252                         while not kidsnumlist[-1]:
253                                 kidsnumlist.pop()
254                                 if kidsnumlist:
255                                         self.dad = self.dad.dad
256                                 else:
257                                         inform('Ignoring unexpected data at end of file.')
258                                         return -1 # bad file with more objects than reported
259                         kidsnumlist[-1] -= 1
260                 if value in AC_OB_TYPES:
261                         new = Obj(AC_OB_TYPES[value])
262                 else:
263                         if value not in AC_OB_BAD_TYPES_LIST:
264                                 AC_OB_BAD_TYPES_LIST.append(value)
265                                 inform('Unexpected object type keyword: "%s". Assuming it is of type: "poly".' % value)
266                         new = Obj(AC_OB_TYPES['poly'])
267                 new.dad = self.dad
268                 new.name = value
269                 self.objlist.append(new)
270
271         def parse_kids(self, value):
272                 kids = int(value)
273                 if kids:
274                         self.kidsnumlist.append(kids)
275                         self.dad = self.objlist[-1]
276                 self.objlist[-1].kids = kids
277
278         def parse_name(self, value):
279                 name = value.split('"')[1]
280                 self.objlist[-1].name = name
281
282         def parse_data(self, value):
283                 data = self.lines[self.i].strip()
284                 self.objlist[-1].data = data
285
286         def parse_tex(self, value):
287                 line = self.lines[self.i - 1] # parse again to properly get paths with spaces
288                 texture = line.split('"')[1]
289                 self.objlist[-1].tex = texture
290
291         def parse_texrep(self, trash):
292                 trep = self.lines[self.i - 1]
293                 trep = trep.split()
294                 trep = [float(trep[1]), float(trep[2])]
295                 self.objlist[-1].texrep = trep
296                 self.objlist[-1].texoff = [0, 0]
297
298         def parse_texoff(self, trash):
299                 toff = self.lines[self.i - 1]
300                 toff = toff.split()
301                 toff = [float(toff[1]), float(toff[2])]
302                 self.objlist[-1].texoff = toff
303                 
304         def parse_mat(self, value):
305                 i = self.i - 1
306                 lines = self.lines
307                 line = lines[i].split()
308                 mat_name = ''
309                 mat_col = mat_amb = mat_emit = mat_spec_col = mat_mir_col = [0,0,0]
310                 mat_alpha = 1
311                 mat_spec = 1.0
312
313                 while line[0] == 'MATERIAL':
314                         mat_name = line[1].split('"')[1]
315                         mat_col = map(float,[line[3],line[4],line[5]])
316                         v = map(float,[line[7],line[8],line[9]])
317                         mat_amb = (v[0]+v[1]+v[2]) / 3.0
318                         v = map(float,[line[11],line[12],line[13]])
319                         mat_emit = (v[0]+v[1]+v[2]) / 3.0
320                         if EMIS_AS_MIRCOL:
321                                 mat_emit = 0
322                                 mat_mir_col = map(float,[line[11],line[12],line[13]])
323
324                         mat_spec_col = map(float,[line[15],line[16],line[17]])
325                         mat_spec = float(line[19]) / 64.0
326                         mat_alpha = float(line[-1])
327                         mat_alpha = 1 - mat_alpha
328                         self.mlist.append([mat_name, mat_col, mat_amb, mat_emit, mat_spec_col, mat_spec, mat_mir_col, mat_alpha])
329                         i += 1
330                         line = lines[i].split()
331
332                 self.i = i
333
334         def parse_rot(self, trash):
335                 i = self.i - 1
336                 ob = self.objlist[-1]
337                 rot = self.lines[i].split(' ', 1)[1]
338                 rot = map(float, rot.split())
339                 matrix = Matrix(rot[:3], rot[3:6], rot[6:])
340                 ob.rot = matrix
341                 size = matrix.scalePart() # vector
342                 ob.size = size
343
344         def parse_loc(self, trash):
345                 i = self.i - 1
346                 loc = self.lines[i].split(' ', 1)[1]
347                 loc = map(float, loc.split())
348                 self.objlist[-1].loc = Vector(loc)
349
350         def parse_crease(self, value):
351                 # AC3D: range is [0.0, 180.0]; Blender: [1, 80]
352                 value = float(value)
353                 self.objlist[-1].crease = int(value)
354
355         def parse_subdiv(self, value):
356                 self.objlist[-1].subdiv = int(value)
357
358         def parse_vert(self, value):
359                 i = self.i
360                 lines = self.lines
361                 obj = self.objlist[-1]
362                 vlist = obj.vlist
363                 n = int(value)
364
365                 while n:
366                         line = lines[i].split()
367                         line = map(float, line)
368                         vlist.append(line)
369                         n -= 1
370                         i += 1
371
372                 if vlist: # prepend a vertex at 1st position to deal with vindex 0 issues
373                         vlist.insert(0, line)
374
375                 self.i = i
376
377         def parse_surf(self, value):
378                 i = self.i
379                 is_smooth = 0
380                 double_sided = 0
381                 lines = self.lines
382                 obj = self.objlist[-1]
383                 vlist = obj.vlist
384                 matlist = obj.matlist
385                 numsurf = int(value)
386                 NUMSURF = numsurf
387
388                 badface_notpoly = badface_multirefs = 0
389
390                 while numsurf:
391                         flags = lines[i].split()[1][2:]
392                         if len(flags) > 1:
393                                 flaghigh = int(flags[0])
394                                 flaglow = int(flags[1])
395                         else:
396                                 flaghigh = 0
397                                 flaglow = int(flags[0])
398
399                         is_smooth = flaghigh & 1
400                         twoside = flaghigh & 2
401                         nextline = lines[i+1].split()
402                         if nextline[0] != 'mat': # the "mat" line may be missing (found in one buggy .ac file)
403                                 matid = 0
404                                 if not matid in matlist: matlist.append(matid)
405                                 i += 2
406                         else:
407                                 matid = int(nextline[1])
408                                 if not matid in matlist: matlist.append(matid)
409                                 nextline = lines[i+2].split()
410                                 i += 3
411                         refs = int(nextline[1])
412                         face = []
413                         faces = []
414                         edges = []
415                         fuv = []
416                         fuvs = []
417                         rfs = refs
418
419                         while rfs:
420                                 line = lines[i].split()
421                                 v = int(line[0]) + 1 # + 1 to avoid vindex == 0
422                                 uv = [float(line[1]), float(line[2])]
423                                 face.append(v)
424                                 fuv.append(Vector(uv))
425                                 rfs -= 1
426                                 i += 1
427
428                         if flaglow: # it's a line or closed line, not a polygon
429                                 while len(face) >= 2:
430                                         cut = face[:2]
431                                         edges.append(cut)
432                                         face = face[1:]
433
434                                 if flaglow == 1 and edges: # closed line
435                                         face = [edges[-1][-1], edges[0][0]]
436                                         edges.append(face)
437
438                         else: # polygon
439
440                                 # check for bad face, that references same vertex more than once
441                                 lenface = len(face)
442                                 if lenface < 3:
443                                         # less than 3 vertices, not a face
444                                         badface_notpoly += 1
445                                 elif sum(map(face.count, face)) != lenface:
446                                         # multiple references to the same vertex
447                                         badface_multirefs += 1
448                                 else: # ok, seems fine
449                                         if len(face) > 4: # ngon, triangulate it
450                                                 polyline = []
451                                                 for vi in face:
452                                                         polyline.append(Vector(vlist[vi]))
453                                                 tris = PolyFill([polyline])
454                                                 for t in tris:
455                                                         tri = [face[t[0]], face[t[1]], face[t[2]]]
456                                                         triuvs = [fuv[t[0]], fuv[t[1]], fuv[t[2]]]
457                                                         faces.append(tri)
458                                                         fuvs.append(triuvs)
459                                         else: # tri or quad
460                                                 faces.append(face)
461                                                 fuvs.append(fuv)
462
463                         obj.flist_cfg.extend([[matid, is_smooth, twoside]] * len(faces))
464                         obj.flist_v.extend(faces)
465                         obj.flist_uv.extend(fuvs)
466                         obj.elist.extend(edges) # loose edges
467
468                         numsurf -= 1      
469
470                 if badface_notpoly or badface_multirefs:
471                         inform('Object "%s" - ignoring bad faces:' % obj.name)
472                         if badface_notpoly:
473                                 inform('\t%d face(s) with less than 3 vertices.' % badface_notpoly)
474                         if badface_multirefs:
475                                 inform('\t%d face(s) with multiple references to a same vertex.' % badface_multirefs)
476
477                 self.i = i
478
479         def parse_file(self):
480                 i = 1
481                 lines = self.lines
482                 line = lines[i].split()
483
484                 while line:
485                         kw = ''
486                         for k in self.token.keys():
487                                 if line[0] == k:
488                                         kw = k
489                                         break
490                         i += 1
491                         if kw:
492                                 self.i = i
493                                 result = self.token[kw](line[1])
494                                 if result:
495                                         break # bad .ac file, stop parsing
496                                 i = self.i
497                         line = lines[i].split()
498
499         # for each group of meshes we try to find one that can be used as
500         # parent of the group in Blender.
501         # If not found, we can use an Empty as parent.
502         def found_parent(self, groupname, olist):
503                 l = [o for o in olist if o.type == AC_POLY \
504                                 and not o.kids and not o.rot and not o.loc]
505                 if l:
506                         for o in l:
507                                 if o.name == groupname:
508                                         return o
509                                 #return l[0]
510                 return None
511
512         def build_hierarchy(self):
513                 blmatrix = AC_TO_BLEND_MATRIX
514
515                 olist = self.objlist[1:]
516                 olist.reverse()
517
518                 scene = self.scene
519
520                 newlist = []
521
522                 for o in olist:
523                         kids = o.kids
524                         if kids:
525                                 children = newlist[-kids:]
526                                 newlist = newlist[:-kids]
527                                 if o.type == AC_GROUP:
528                                         parent = self.found_parent(o.name, children)
529                                         if parent:
530                                                 children.remove(parent)
531                                                 o.bl_obj = parent.bl_obj
532                                         else: # not found, use an empty
533                                                 empty = scene.objects.new('Empty', o.name)
534                                                 o.bl_obj = empty
535
536                                 bl_children = [c.bl_obj for c in children if c.bl_obj != None]
537                                 
538                                 o.bl_obj.makeParent(bl_children, 0, 1)
539                                 for child in children:
540                                         blob = child.bl_obj
541                                         if not blob: continue
542                                         if child.rot:
543                                                 eul = euler_in_radians(child.rot.toEuler())
544                                                 blob.setEuler(eul)
545                                         if child.size:
546                                                 blob.size = child.size
547                                         if not child.loc:
548                                                 child.loc = Vector(0.0, 0.0, 0.0)
549                                         blob.setLocation(child.loc)
550
551                         newlist.append(o)
552
553                 for o in newlist: # newlist now only has objs w/o parents
554                         blob = o.bl_obj
555                         if not blob:
556                                 continue
557                         if o.size:
558                                 o.bl_obj.size = o.size
559                         if not o.rot:
560                                 blob.setEuler([1.5707963267948966, 0, 0])
561                         else:
562                                 matrix = o.rot * blmatrix
563                                 eul = euler_in_radians(matrix.toEuler())
564                                 blob.setEuler(eul)
565                         if o.loc:
566                                 o.loc *= blmatrix
567                         else:
568                                 o.loc = Vector(0.0, 0.0, 0.0)
569                         blob.setLocation(o.loc) # forces DAG update, so we do it even for 0, 0, 0
570
571                 # XXX important: until we fix the BPy API so it doesn't increase user count
572                 # when wrapping a Blender object, this piece of code is needed for proper
573                 # object (+ obdata) deletion in Blender:
574                 for o in self.objlist:
575                         if o.bl_obj:
576                                 o.bl_obj = None
577
578         def testAC3DImport(self):
579
580                 FACE_TWOSIDE = Mesh.FaceModes['TWOSIDE']
581                 FACE_TEX = Mesh.FaceModes['TEX']
582                 MESH_AUTOSMOOTH = Mesh.Modes['AUTOSMOOTH']
583
584                 MAT_MODE_ZTRANSP = Material.Modes['ZTRANSP']
585                 MAT_MODE_TRANSPSHADOW = Material.Modes['TRANSPSHADOW']
586
587                 scene = self.scene
588
589                 bl_images = {} # loaded texture images
590                 missing_textures = [] # textures we couldn't find
591
592                 objlist = self.objlist[1:] # skip 'world'
593
594                 bmat = []
595                 has_transp_mats = False
596                 for mat in self.mlist:
597                         name = mat[0]
598                         m = Material.New(name)
599                         m.rgbCol = (mat[1][0], mat[1][1], mat[1][2])
600                         m.amb = mat[2]
601                         m.emit = mat[3]
602                         m.specCol = (mat[4][0], mat[4][1], mat[4][2])
603                         m.spec = mat[5]
604                         m.mirCol = (mat[6][0], mat[6][1], mat[6][2])
605                         m.alpha = mat[7]
606                         if m.alpha < 1.0:
607                                 m.mode |= MAT_MODE_ZTRANSP
608                                 has_transp_mats = True
609                         bmat.append(m)
610
611                 if has_transp_mats:
612                         for mat in bmat:
613                                 mat.mode |= MAT_MODE_TRANSPSHADOW
614
615                 obj_idx = 0 # index of current obj in loop
616                 for obj in objlist:
617                         if obj.type == AC_GROUP:
618                                 continue
619                         elif obj.type == AC_LIGHT:
620                                 light = Lamp.New('Lamp')
621                                 object = scene.objects.new(light, obj.name)
622                                 #object.select(True)
623                                 obj.bl_obj = object
624                                 if obj.data:
625                                         light.name = obj.data
626                                 continue
627
628                         # type AC_POLY:
629
630                         # old .ac files used empty meshes as groups, convert to a real ac group
631                         if not obj.vlist and obj.kids:
632                                 obj.type = AC_GROUP
633                                 continue
634
635                         mesh = Mesh.New()
636                         object = scene.objects.new(mesh, obj.name)
637                         #object.select(True)
638                         obj.bl_obj = object
639                         if obj.data: mesh.name = obj.data
640                         mesh.degr = obj.crease # will auto clamp to [1, 80]
641
642                         if not obj.vlist: # no vertices? nothing more to do
643                                 continue
644
645                         mesh.verts.extend(obj.vlist)
646
647                         objmat_indices = []
648                         for mat in bmat:
649                                 if bmat.index(mat) in obj.matlist:
650                                         objmat_indices.append(bmat.index(mat))
651                                         mesh.materials += [mat]
652                                         if DISPLAY_TRANSP and mat.alpha < 1.0:
653                                                 object.transp = True
654
655                         for e in obj.elist:
656                                 mesh.edges.extend(e)
657
658                         if obj.flist_v:
659                                 mesh.faces.extend(obj.flist_v)
660
661                                 facesnum = len(mesh.faces)
662
663                                 if facesnum == 0: # shouldn't happen, of course
664                                         continue
665
666                                 mesh.faceUV = True
667
668                                 # checking if the .ac file had duplicate faces (Blender ignores them)
669                                 if facesnum != len(obj.flist_v):
670                                         # it has, ugh. Let's clean the uv list:
671                                         lenfl = len(obj.flist_v)
672                                         flist = obj.flist_v
673                                         uvlist = obj.flist_uv
674                                         cfglist = obj.flist_cfg
675                                         for f in flist:
676                                                 f.sort()
677                                         fi = lenfl
678                                         while fi > 0: # remove data related to duplicates
679                                                 fi -= 1
680                                                 if flist[fi] in flist[:fi]:
681                                                         uvlist.pop(fi)
682                                                         cfglist.pop(fi)
683
684                                 img = None
685                                 if obj.tex != '':
686                                         if obj.tex in bl_images.keys():
687                                                 img = bl_images[obj.tex]
688                                         elif obj.tex not in missing_textures:
689                                                 texfname = None
690                                                 objtex = obj.tex
691                                                 baseimgname = bsys.basename(objtex)
692                                                 if bsys.exists(objtex) == 1:
693                                                         texfname = objtex
694                                                 elif bsys.exists(bsys.join(self.importdir, objtex)):
695                                                         texfname = bsys.join(self.importdir, objtex)
696                                                 else:
697                                                         if baseimgname.find('\\') > 0:
698                                                                 baseimgname = bsys.basename(objtex.replace('\\','/'))
699                                                         objtex = bsys.join(self.importdir, baseimgname)
700                                                         if bsys.exists(objtex) == 1:
701                                                                 texfname = objtex
702                                                         else:
703                                                                 objtex = bsys.join(TEXTURES_DIR, baseimgname)
704                                                                 if bsys.exists(objtex):
705                                                                         texfname = objtex
706                                                 if texfname:
707                                                         try:
708                                                                 img = Image.Load(texfname)
709                                                                 # Commented because it's unnecessary:
710                                                                 #img.xrep = int(obj.texrep[0])
711                                                                 #img.yrep = int(obj.texrep[1])
712                                                                 if img:
713                                                                         bl_images[obj.tex] = img
714                                                         except:
715                                                                 inform("Couldn't load texture: %s" % baseimgname)
716                                                 else:
717                                                         missing_textures.append(obj.tex)
718                                                         inform("Couldn't find texture: %s" % baseimgname)
719
720                                 for i in range(facesnum):
721                                         f = obj.flist_cfg[i]
722                                         fmat = f[0]
723                                         is_smooth = f[1]
724                                         twoside = f[2]
725                                         bface = mesh.faces[i]
726                                         bface.smooth = is_smooth
727                                         if twoside: bface.mode |= FACE_TWOSIDE
728                                         if img:
729                                                 bface.mode |= FACE_TEX
730                                                 bface.image = img
731                                         bface.mat = objmat_indices.index(fmat)
732                                         fuv = obj.flist_uv[i]
733                                         if obj.texoff:
734                                                 uoff = obj.texoff[0]
735                                                 voff = obj.texoff[1]
736                                                 urep = obj.texrep[0]
737                                                 vrep = obj.texrep[1]
738                                                 for uv in fuv:
739                                                         uv[0] *= urep
740                                                         uv[1] *= vrep
741                                                         uv[0] += uoff
742                                                         uv[1] += voff
743
744                                         mesh.faces[i].uv = fuv
745
746                                 # finally, delete the 1st vertex we added to prevent vindices == 0
747                                 mesh.verts.delete(0)
748
749                                 mesh.calcNormals()
750
751                                 mesh.mode = MESH_AUTOSMOOTH
752
753                                 # subdiv: create SUBSURF modifier in Blender
754                                 if SUBDIV and obj.subdiv > 0:
755                                         subdiv = obj.subdiv
756                                         subdiv_render = subdiv
757                                         # just to be safe:
758                                         if subdiv_render > 6: subdiv_render = 6
759                                         if subdiv > 3: subdiv = 3
760                                         modif = object.modifiers.append(Modifier.Types.SUBSURF)
761                                         modif[Modifier.Settings.LEVELS] = subdiv
762                                         modif[Modifier.Settings.RENDLEVELS] = subdiv_render
763
764                         obj_idx += 1
765
766                 self.build_hierarchy()
767                 scene.update()
768
769 # End of class AC3DImport
770
771 def filesel_callback(filename):
772
773         inform("\nTrying to import AC3D model(s) from:\n%s ..." % filename)
774         Window.WaitCursor(1)
775         starttime = bsys.time()
776         test = AC3DImport(filename)
777         Window.WaitCursor(0)
778         endtime = bsys.time() - starttime
779         inform('Done! Data imported in %.3f seconds.\n' % endtime)
780
781 Window.EditMode(0)
782
783 Window.FileSelector(filesel_callback, "Import AC3D", "*.ac")