Merge branch 'cb/fixs'
[plumiferos.git] / release / scripts / ac3d_import.py
blob275735b57bd8863c01298f24f75cc2743128fe51
1 #!BPY
3 """ Registration info for Blender menus:
4 Name: 'AC3D (.ac)...'
5 Blender: 243
6 Group: 'Import'
7 Tip: 'Import an AC3D (.ac) file.'
8 """
10 __author__ = "Willian P. Germano"
11 __url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org",
12 "PLib 3d gaming lib, http://plib.sf.net")
13 __version__ = "2.43.1 2007-02-21"
15 __bpydoc__ = """\
16 This script imports AC3D models into Blender.
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.
22 Supported:<br>
23 UV-textured meshes with hierarchy (grouping) information.
25 Missing:<br>
26 The url tag is irrelevant for Blender.
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>
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.
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 """
43 # $Id: ac3d_import.py 10128 2007-02-22 20:19:58Z ianwill $
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 *****
57 # Copyright (C) 2004-2007: Willian P. Germano, wgermano _at_ ig.com.br
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.
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.
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.
73 # ***** END GPL LICENCE BLOCK *****
74 # --------------------------------------------------------------------------
76 from math import radians
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
84 # Default folder for AC3D textures, to override wrong paths, change to your
85 # liking or leave as "":
86 TEXTURES_DIR = ""
88 DISPLAY_TRANSP = True
90 SUBDIV = True
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.'
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)
103 rd = Registry.GetKey('ac3d_import', True)
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()
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()
123 VERBOSE = True
124 rd = Registry.GetKey('General', True)
125 if rd:
126 if rd.has_key('verbose'):
127 VERBOSE = rd['verbose']
130 errmsg = ""
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])
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
147 AC_OB_BAD_TYPES_LIST = [] # to hold references to unknown (wrong) ob types
149 def inform(msg):
150 global VERBOSE
151 if VERBOSE: print msg
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
160 class Obj:
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
183 self.bl_obj = None # the actual Blender object created from this data
185 class AC3DImport:
187 def __init__(self, filename):
189 global errmsg
191 self.scene = Scene.GetCurrent()
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.')
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}
230 self.objlist = []
231 self.mlist = []
232 self.kidsnumlist = []
233 self.dad = None
235 self.lines = file.readlines()
236 self.lines.append('')
237 self.parse_file()
238 file.close()
240 self.testAC3DImport()
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)
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
271 def parse_name(self, value):
272 name = value.split('"')[1]
273 self.objlist[-1].name = name
275 def parse_data(self, value):
276 data = self.lines[self.i].strip()
277 self.objlist[-1].data = data
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
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]
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
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
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()
321 self.i = i
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
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)
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)
344 def parse_subdiv(self, value):
345 self.objlist[-1].subdiv = int(value)
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)
354 while n:
355 line = lines[i].split()
356 line = map(float, line)
357 vlist.append(line)
358 n -= 1
359 i += 1
361 if vlist: # prepend a vertex at 1st position to deal with vindex 0 issues
362 vlist.insert(0, line)
364 self.i = i
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
377 badface_notpoly = badface_multirefs = 0
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])
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
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
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:]
423 if flaglow == 1 and edges: # closed line
424 face = [edges[-1][-1], edges[0][0]]
425 edges.append(face)
427 else: # polygon
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)
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
457 numsurf -= 1
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)
466 self.i = i
468 def parse_file(self):
469 i = 1
470 lines = self.lines
471 line = lines[i].split()
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()
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
501 def build_hierarchy(self):
502 blmatrix = AC_TO_BLEND_MATRIX
504 olist = self.objlist[1:]
505 olist.reverse()
507 scene = self.scene
509 newlist = []
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
525 bl_children = [c.bl_obj for c in children if c.bl_obj != None]
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)
540 newlist.append(o)
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
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
567 def testAC3DImport(self):
569 FACE_TWOSIDE = Mesh.FaceModes['TWOSIDE']
570 FACE_TEX = Mesh.FaceModes['TEX']
571 MESH_AUTOSMOOTH = Mesh.Modes['AUTOSMOOTH']
573 MAT_MODE_ZTRANSP = Material.Modes['ZTRANSP']
574 MAT_MODE_TRANSPSHADOW = Material.Modes['TRANSPSHADOW']
576 scene = self.scene
578 bl_images = {} # loaded texture images
579 missing_textures = [] # textures we couldn't find
581 objlist = self.objlist[1:] # skip 'world'
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)
599 if has_transp_mats:
600 for mat in bmat:
601 mat.mode |= MAT_MODE_TRANSPSHADOW
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
616 # type AC_POLY:
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
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]
630 if not obj.vlist: # no vertices? nothing more to do
631 continue
633 mesh.verts.extend(obj.vlist)
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
643 for e in obj.elist:
644 mesh.edges.extend(e)
646 if obj.flist_v:
647 mesh.faces.extend(obj.flist_v)
649 facesnum = len(mesh.faces)
651 if facesnum == 0: # shouldn't happen, of course
652 continue
654 mesh.faceUV = True
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)
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)
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
732 mesh.faces[i].uv = fuv
734 # finally, delete the 1st vertex we added to prevent vindices == 0
735 mesh.verts.delete(0)
737 mesh.calcNormals()
739 mesh.mode = MESH_AUTOSMOOTH
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
752 obj_idx += 1
754 self.build_hierarchy()
755 scene.update()
757 # End of class AC3DImport
759 def filesel_callback(filename):
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)
769 Window.EditMode(0)
771 Window.FileSelector(filesel_callback, "Import AC3D", "*.ac")