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