Added Image.get_abs_filename() and updated scripts to use it. This removes the necess...
[blender.git] / release / io / import_3ds.py
1 #!BPY
2 """ 
3 Name: '3D Studio (.3ds)...'
4 Blender: 244
5 Group: 'Import'
6 Tooltip: 'Import from 3DS file format (.3ds)'
7 """
8
9 __author__= ['Bob Holcomb', 'Richard L?rk?ng', 'Damien McGinnes', 'Campbell Barton', 'Mario Lapin']
10 __url__ = ("blenderartists.org", "www.blender.org", "www.gametutorials.com", "lib3ds.sourceforge.net/")
11 __version__= '0.996'
12 __bpydoc__= '''\
13
14 3ds Importer
15
16 This script imports a 3ds file and the materials into Blender for editing.
17
18 Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen).
19
20 0.996 by Mario Lapin (mario.lapin@gmail.com) 13/04/200 <br>
21  - Implemented workaround to correct association between name, geometry and materials of
22    imported meshes.
23    
24    Without this patch, version 0.995 of this importer would associate to each mesh object the
25    geometry and the materials of the previously parsed mesh object. By so, the name of the
26    first mesh object would be thrown away, and the name of the last mesh object would be
27    automatically merged with a '.001' at the end. No object would desappear, however object's
28    names and materials would be completely jumbled.
29
30 0.995 by Campbell Barton<br>
31 - workaround for buggy mesh vert delete
32 - minor tweaks
33
34 0.99 by Bob Holcomb<br>
35 - added support for floating point color values that previously broke on import.
36
37 0.98 by Campbell Barton<br>
38 - import faces and verts to lists instead of a mesh, convert to a mesh later
39 - use new index mapping feature of mesh to re-map faces that were not added.
40
41 0.97 by Campbell Barton<br>
42 - Strip material names of spaces
43 - Added import as instance to import the 3ds into its own
44   scene and add a group instance to the current scene
45 - New option to scale down imported objects so they are within a limited bounding area.
46
47 0.96 by Campbell Barton<br>
48 - Added workaround for bug in setting UV's for Zero vert index UV faces.
49 - Removed unique name function, let blender make the names unique.
50
51 0.95 by Campbell Barton<br>
52 - Removed workarounds for Blender 2.41
53 - Mesh objects split by material- many 3ds objects used more then 16 per mesh.
54 - Removed a lot of unneeded variable creation.
55
56 0.94 by Campbell Barton<br> 
57 - Face import tested to be about overall 16x speedup over 0.93.
58 - Material importing speedup.
59 - Tested with more models.
60 - Support some corrupt models.
61
62 0.93 by Campbell Barton<br> 
63 - Tested with 400 3ds files from turbosquid and samples.
64 - Tactfully ignore faces that used the same verts twice.
65 - Rollback to 0.83 sloppy un-reorganized code, this broke UV coord loading.
66 - Converted from NMesh to Mesh.
67 - Faster and cleaner new names.
68 - Use external comprehensive image loader.
69 - Re intergrated 0.92 and 0.9 changes
70 - Fixes for 2.41 compat.
71 - Non textured faces do not use a texture flag.
72
73 0.92<br>
74 - Added support for diffuse, alpha, spec, bump maps in a single material
75
76 0.9<br>
77 - Reorganized code into object/material block functions<br>
78 - Use of Matrix() to copy matrix data<br>
79 - added support for material transparency<br>
80
81 0.83 2005-08-07: Campell Barton
82 -  Aggressive image finding and case insensitivy for posisx systems.
83
84 0.82a 2005-07-22
85 - image texture loading (both for face uv and renderer)
86
87 0.82 - image texture loading (for face uv)
88
89 0.81a (fork- not 0.9) Campbell Barton 2005-06-08
90 - Simplified import code
91 - Never overwrite data
92 - Faster list handling
93 - Leaves import selected
94
95 0.81 Damien McGinnes 2005-01-09
96 - handle missing images better
97     
98 0.8 Damien McGinnes 2005-01-08
99 - copies sticky UV coords to face ones
100 - handles images better
101 - Recommend that you run 'RemoveDoubles' on each imported mesh after using this script
102
103 '''
104
105 # ***** BEGIN GPL LICENSE BLOCK *****
106 #
107 # Script copyright (C) Bob Holcomb 
108 #
109 # This program is free software; you can redistribute it and/or
110 # modify it under the terms of the GNU General Public License
111 # as published by the Free Software Foundation; either version 2
112 # of the License, or (at your option) any later version.
113 #
114 # This program is distributed in the hope that it will be useful,
115 # but WITHOUT ANY WARRANTY; without even the implied warranty of
116 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
117 # GNU General Public License for more details.
118 #
119 # You should have received a copy of the GNU General Public License
120 # along with this program; if not, write to the Free Software Foundation,
121 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
122 #
123 # ***** END GPL LICENCE BLOCK *****
124 # --------------------------------------------------------------------------
125
126 # Importing modules
127
128 import os
129 import time
130 import struct
131
132 from import_obj import unpack_face_list, load_image
133
134 import bpy
135 import Mathutils
136
137 # import Blender
138 # from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils
139 # from Blender.Mathutils import Vector
140 # import BPyImage
141
142 # import BPyMessages
143
144 # try:
145 #       from struct import calcsize, unpack
146 # except:
147 #       calcsize= unpack= None
148
149
150
151 # # If python version is less than 2.4, try to get set stuff from module
152 # try:
153 #       set
154 # except:
155 #       from sets import Set as set
156
157 BOUNDS_3DS = []
158
159
160 #this script imports uvcoords as sticky vertex coords
161 #this parameter enables copying these to face uv coords
162 #which shold be more useful.
163
164 def createBlenderTexture(material, name, image):
165         texture = bpy.data.textures.new(name)
166         texture.setType('Image')
167         texture.image = image
168         material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL)
169
170
171
172 ######################################################
173 # Data Structures
174 ######################################################
175
176 #Some of the chunks that we will see
177 #----- Primary Chunk, at the beginning of each file
178 PRIMARY = int('0x4D4D',16)
179
180 #------ Main Chunks
181 OBJECTINFO   =      int('0x3D3D',16);      #This gives the version of the mesh and is found right before the material and object information
182 VERSION      =      int('0x0002',16);      #This gives the version of the .3ds file
183 EDITKEYFRAME=      int('0xB000',16);      #This is the header for all of the key frame info
184
185 #------ sub defines of OBJECTINFO
186 MATERIAL = 45055                #0xAFFF                         // This stored the texture info
187 OBJECT = 16384          #0x4000                         // This stores the faces, vertices, etc...
188
189 #>------ sub defines of MATERIAL
190 #------ sub defines of MATERIAL_BLOCK
191 MAT_NAME                =       int('0xA000',16)        # This holds the material name
192 MAT_AMBIENT             =       int('0xA010',16)        # Ambient color of the object/material
193 MAT_DIFFUSE             =       int('0xA020',16)        # This holds the color of the object/material
194 MAT_SPECULAR    =       int('0xA030',16)        # SPecular color of the object/material
195 MAT_SHINESS             =       int('0xA040',16)        # ??
196 MAT_TRANSPARENCY=       int('0xA050',16)        # Transparency value of material
197 MAT_SELF_ILLUM  =       int('0xA080',16)        # Self Illumination value of material
198 MAT_WIRE                =       int('0xA085',16)        # Only render's wireframe
199
200 MAT_TEXTURE_MAP =       int('0xA200',16)        # This is a header for a new texture map
201 MAT_SPECULAR_MAP=       int('0xA204',16)        # This is a header for a new specular map
202 MAT_OPACITY_MAP =       int('0xA210',16)        # This is a header for a new opacity map
203 MAT_REFLECTION_MAP=     int('0xA220',16)        # This is a header for a new reflection map
204 MAT_BUMP_MAP    =       int('0xA230',16)        # This is a header for a new bump map
205 MAT_MAP_FILENAME =      int('0xA300',16)      # This holds the file name of the texture
206
207 MAT_FLOAT_COLOR = int ('0x0010', 16) #color defined as 3 floats
208 MAT_24BIT_COLOR = int ('0x0011', 16) #color defined as 3 bytes
209
210 #>------ sub defines of OBJECT
211 OBJECT_MESH  =      int('0x4100',16);      # This lets us know that we are reading a new object
212 OBJECT_LAMP =      int('0x4600',16);      # This lets un know we are reading a light object
213 OBJECT_LAMP_SPOT = int('0x4610',16);            # The light is a spotloght.
214 OBJECT_LAMP_OFF = int('0x4620',16);             # The light off.
215 OBJECT_LAMP_ATTENUATE = int('0x4625',16);       
216 OBJECT_LAMP_RAYSHADE = int('0x4627',16);        
217 OBJECT_LAMP_SHADOWED = int('0x4630',16);        
218 OBJECT_LAMP_LOCAL_SHADOW = int('0x4640',16);    
219 OBJECT_LAMP_LOCAL_SHADOW2 = int('0x4641',16);   
220 OBJECT_LAMP_SEE_CONE = int('0x4650',16);        
221 OBJECT_LAMP_SPOT_RECTANGULAR = int('0x4651',16);
222 OBJECT_LAMP_SPOT_OVERSHOOT = int('0x4652',16);
223 OBJECT_LAMP_SPOT_PROJECTOR = int('0x4653',16);
224 OBJECT_LAMP_EXCLUDE = int('0x4654',16);
225 OBJECT_LAMP_RANGE = int('0x4655',16);
226 OBJECT_LAMP_ROLL = int('0x4656',16);
227 OBJECT_LAMP_SPOT_ASPECT = int('0x4657',16);
228 OBJECT_LAMP_RAY_BIAS = int('0x4658',16);
229 OBJECT_LAMP_INNER_RANGE = int('0x4659',16);
230 OBJECT_LAMP_OUTER_RANGE = int('0x465A',16);
231 OBJECT_LAMP_MULTIPLIER = int('0x465B',16);
232 OBJECT_LAMP_AMBIENT_LIGHT = int('0x4680',16);
233
234
235
236 OBJECT_CAMERA=      int('0x4700',16);      # This lets un know we are reading a camera object
237
238 #>------ sub defines of CAMERA
239 OBJECT_CAM_RANGES=   int('0x4720',16);      # The camera range values
240
241 #>------ sub defines of OBJECT_MESH
242 OBJECT_VERTICES =   int('0x4110',16);      # The objects vertices
243 OBJECT_FACES    =   int('0x4120',16);      # The objects faces
244 OBJECT_MATERIAL =   int('0x4130',16);      # This is found if the object has a material, either texture map or color
245 OBJECT_UV       =   int('0x4140',16);      # The UV texture coordinates
246 OBJECT_TRANS_MATRIX  =   int('0x4160',16); # The Object Matrix
247
248 global scn
249 scn = None
250
251 #the chunk class
252 class chunk:
253         ID = 0
254         length = 0
255         bytes_read = 0
256
257         #we don't read in the bytes_read, we compute that
258         binary_format='<HI'
259
260         def __init__(self):
261                 self.ID = 0
262                 self.length = 0
263                 self.bytes_read = 0
264
265         def dump(self):
266                 print('ID: ', self.ID)
267                 print('ID in hex: ', hex(self.ID))
268                 print('length: ', self.length)
269                 print('bytes_read: ', self.bytes_read)
270
271 def read_chunk(file, chunk):
272         temp_data = file.read(struct.calcsize(chunk.binary_format))
273         data = struct.unpack(chunk.binary_format, temp_data)
274         chunk.ID = data[0]
275         chunk.length = data[1]
276         #update the bytes read function
277         chunk.bytes_read = 6
278
279         #if debugging
280         #chunk.dump()
281
282 def read_string(file):
283         #read in the characters till we get a null character
284         s = b''
285 #       s = ''
286         while not s.endswith(b'\x00'):
287 #       while not s.endswith('\x00'):
288                 s += struct.unpack('<c', file.read(1))[0]
289 #               s += struct.unpack( '<c', file.read(1) )[0]
290                 #print 'string: ',s
291
292         s = str(s[:-1], 'ASCII')
293 #       print("read string", s)
294
295         #remove the null character from the string
296         return s
297 #       return s[:-1]
298
299 ######################################################
300 # IMPORT
301 ######################################################
302 def process_next_object_chunk(file, previous_chunk):
303         new_chunk = chunk()
304         temp_chunk = chunk()
305
306         while (previous_chunk.bytes_read < previous_chunk.length):
307                 #read the next chunk
308                 read_chunk(file, new_chunk)
309
310 def skip_to_end(file, skip_chunk):
311         buffer_size = skip_chunk.length - skip_chunk.bytes_read
312         binary_format='%ic' % buffer_size
313         temp_data = file.read(struct.calcsize(binary_format))
314         skip_chunk.bytes_read += buffer_size
315
316
317 def add_texture_to_material(image, texture, material, mapto):
318 #       if mapto=='DIFFUSE':
319 #               map = Texture.MapTo.COL
320 #       elif mapto=='SPECULAR':
321 #               map = Texture.MapTo.SPEC
322 #       elif mapto=='OPACITY':
323 #               map = Texture.MapTo.ALPHA
324 #       elif mapto=='BUMP':
325 #               map = Texture.MapTo.NOR
326 #       else:
327         if mapto not in ("COLOR", "SPECULARITY", "ALPHA", "NORMAL"):
328                 print('/tError:  Cannot map to "%s"\n\tassuming diffuse color. modify material "%s" later.' % (mapto, material.name))
329                 mapto = "COLOR"
330 #               map = Texture.MapTo.COL
331
332         if image: texture.image = image
333 #       if image: texture.setImage(image) # double check its an image.
334
335         material.add_texture(texture, "UV", mapto)
336 #       free_tex_slots = [i for i, tex in enumerate( material.getTextures() ) if tex == None]
337 #       if not free_tex_slots:
338 #               print('/tError: Cannot add "%s" map. 10 Texture slots alredy used.' % mapto)
339 #       else:
340 #               material.setTexture(free_tex_slots[0],texture,Texture.TexCo.UV,map)
341
342
343 def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
344         #print previous_chunk.bytes_read, 'BYTES READ'
345         contextObName = None
346         contextLamp = [None, None] # object, Data
347         contextMaterial = None
348         contextMatrix_rot = None # Blender.Mathutils.Matrix(); contextMatrix.identity()
349         #contextMatrix_tx = None # Blender.Mathutils.Matrix(); contextMatrix.identity()
350         contextMesh_vertls = None
351         contextMesh_facels = None
352         contextMeshMaterials = {} # matname:[face_idxs]
353         contextMeshUV = None
354         
355         TEXTURE_DICT = {}
356         MATDICT = {}
357 #       TEXMODE = Mesh.FaceModes['TEX']
358         
359         # Localspace variable names, faster.
360         STRUCT_SIZE_1CHAR = struct.calcsize('c')
361         STRUCT_SIZE_2FLOAT = struct.calcsize('2f')
362         STRUCT_SIZE_3FLOAT = struct.calcsize('3f')
363         STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize('H')
364         STRUCT_SIZE_4UNSIGNED_SHORT = struct.calcsize('4H')
365         STRUCT_SIZE_4x3MAT = struct.calcsize('ffffffffffff')
366         _STRUCT_SIZE_4x3MAT = struct.calcsize('fffffffffffff')
367         # STRUCT_SIZE_4x3MAT = calcsize('ffffffffffff')
368         # print STRUCT_SIZE_4x3MAT, ' STRUCT_SIZE_4x3MAT'
369         
370         def putContextMesh(myContextMesh_vertls, myContextMesh_facels, myContextMeshMaterials):
371                 
372                 materialFaces = set() # faces that have a material. Can optimize?
373                 
374                 # Now make copies with assigned materils.
375                 
376                 def makeMeshMaterialCopy(matName, faces):                       
377                         '''
378                         Make a new mesh with only face the faces that use this material.
379                         faces can be any iterable object - containing ints.
380                         '''
381                         
382                         faceVertUsers = [False] * len(myContextMesh_vertls)
383                         ok = 0
384                         for fIdx in faces:
385                                 for vindex in myContextMesh_facels[fIdx]:
386                                         faceVertUsers[vindex] = True
387                                         if matName != None: # if matName is none then this is a set(), meaning we are using the untextured faces and do not need to store textured faces.
388                                                 materialFaces.add(fIdx)
389                                         ok = 1
390                         
391                         if not ok:
392                                 return
393                                         
394                         myVertMapping = {}
395                         vertMappingIndex = 0
396                         
397                         vertsToUse = [i for i in range(len(myContextMesh_vertls)) if faceVertUsers[i]]
398                         myVertMapping = dict( [ (ii, i) for i, ii in enumerate(vertsToUse) ] )
399                         
400                         tempName= '%s_%s' % (contextObName, matName) # matName may be None.
401                         bmesh = bpy.data.add_mesh(tempName)
402 #                       bmesh = bpy.data.meshes.new(tempName)
403                         
404                         if matName == None:
405                                 img = None
406                         else:
407                                 bmat = MATDICT[matName][1]
408                                 bmesh.add_material(bmat)
409 #                               bmesh.materials = [bmat]
410                                 try:    img = TEXTURE_DICT[bmat.name]
411                                 except: img = None
412                                 
413 #                       bmesh_verts = bmesh.verts
414                         if len(vertsToUse):
415                                 bmesh.add_geometry(len(vertsToUse), 0, len(faces))
416
417                                 # XXX why add extra vertex?
418 #                               bmesh_verts.extend( [Vector()] )
419                                 bmesh.verts.foreach_set("co", [x for tup in [myContextMesh_vertls[i] for i in vertsToUse] for x in tup])
420 #                               bmesh_verts.extend( [myContextMesh_vertls[i] for i in vertsToUse] )
421
422                                 # +1 because of DUMMYVERT
423                                 bmesh.faces.foreach_set("verts", unpack_face_list([[myVertMapping[vindex] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces]))
424 #                               face_mapping = bmesh.faces.extend( [ [ bmesh_verts[ myVertMapping[vindex]+1] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces ], indexList=True )
425
426                                 if bmesh.faces and (contextMeshUV or img):
427                                         bmesh.add_uv_texture()
428 #                                       bmesh.faceUV = 1
429                                         for ii, i in enumerate(faces):
430
431                                                 # Mapped index- faces may have not been added- if so, then map to the correct index
432                                                 # BUGGY API - face_mapping is not always the right length
433 #                                               map_index = face_mapping[ii]
434
435                                                 if 1:
436 #                                               if map_index != None:
437                                                         targetFace = bmesh.faces[ii]
438 #                                                       targetFace = bmesh.faces[map_index]
439
440                                                         uf = bmesh.active_uv_texture.data[ii]
441
442                                                         if contextMeshUV:
443                                                                 # v.index-1 because of the DUMMYVERT
444                                                                 uvs = [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]]
445
446                                                                 if len(myContextMesh_facels[i]) == 3:
447                                                                         uf.uv1, uf.uv2, uf.uv3, uf.uv4 = uvs + [(0.0, 0.0)]
448                                                                 else:
449                                                                         uf.uv1, uf.uv2, uf.uv3, uf.uv4 = uvs
450 #                                                               targetFace.uv = [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]]
451                                                         if img:
452                                                                 uf.image = img
453 #                                                               targetFace.image = img
454                         
455                         # bmesh.transform(contextMatrix)
456                         ob = bpy.data.add_object("MESH", tempName)
457                         ob.data = bmesh
458                         SCN.add_object(ob)
459 #                       ob = SCN_OBJECTS.new(bmesh, tempName)
460                         '''
461                         if contextMatrix_tx:
462                                 ob.setMatrix(contextMatrix_tx)
463                         '''
464                         
465                         if contextMatrix_rot:
466                                 # ob.matrix = [x for row in contextMatrix_rot for x in row]
467                                 ob.matrix = contextMatrix_rot
468 #                               ob.setMatrix(contextMatrix_rot)
469                         
470                         importedObjects.append(ob)
471                         bmesh.update()
472 #                       bmesh.calcNormals()
473                 
474                 for matName, faces in myContextMeshMaterials.items():
475                         makeMeshMaterialCopy(matName, faces)
476                         
477                 if len(materialFaces) != len(myContextMesh_facels):
478                         # Invert material faces.
479                         makeMeshMaterialCopy(None, set(range(len( myContextMesh_facels ))) - materialFaces)
480                         #raise 'Some UnMaterialed faces', len(contextMesh.faces)
481         
482         #a spare chunk
483         new_chunk = chunk()
484         temp_chunk = chunk()
485         
486         CreateBlenderObject = False
487
488         def read_float_color(temp_chunk):
489                 temp_data = file.read(struct.calcsize('3f'))
490                 temp_chunk.bytes_read += 12
491                 return [float(col) for col in struct.unpack('<3f', temp_data)]
492
493         def read_byte_color(temp_chunk):
494                 temp_data = file.read(struct.calcsize('3B'))
495                 temp_chunk.bytes_read += 3
496                 return [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb
497
498         def read_texture(new_chunk, temp_chunk, name, mapto):
499                 new_texture = bpy.data.add_texture('Diffuse')
500                 new_texture.type = 'IMAGE'
501
502                 img = None
503                 while (new_chunk.bytes_read < new_chunk.length):
504                         #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length
505                         read_chunk(file, temp_chunk)
506
507                         if (temp_chunk.ID == MAT_MAP_FILENAME):
508                                 texture_name = read_string(file)
509                                 img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname)
510                                 new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
511
512                         else:
513                                 skip_to_end(file, temp_chunk)
514
515                         new_chunk.bytes_read += temp_chunk.bytes_read
516
517                 # add the map to the material in the right channel
518                 if img:
519                         add_texture_to_material(img, new_texture, contextMaterial, mapto)
520
521         dirname = os.path.dirname(FILENAME)
522
523         #loop through all the data for this chunk (previous chunk) and see what it is
524         while (previous_chunk.bytes_read < previous_chunk.length):
525                 #print '\t', previous_chunk.bytes_read, 'keep going'
526                 #read the next chunk
527                 #print 'reading a chunk'
528                 read_chunk(file, new_chunk)
529
530                 #is it a Version chunk?
531                 if (new_chunk.ID == VERSION):
532                         #print 'if (new_chunk.ID == VERSION):'
533                         #print 'found a VERSION chunk'
534                         #read in the version of the file
535                         #it's an unsigned short (H)
536                         temp_data = file.read(struct.calcsize('I'))
537                         version = struct.unpack('<I', temp_data)[0]
538                         new_chunk.bytes_read += 4 #read the 4 bytes for the version number
539                         #this loader works with version 3 and below, but may not with 4 and above
540                         if (version > 3):
541                                 print('\tNon-Fatal Error:  Version greater than 3, may not load correctly: ', version)
542
543                 #is it an object info chunk?
544                 elif (new_chunk.ID == OBJECTINFO):
545                         #print 'elif (new_chunk.ID == OBJECTINFO):'
546                         # print 'found an OBJECTINFO chunk'
547                         process_next_chunk(file, new_chunk, importedObjects, IMAGE_SEARCH)
548                         
549                         #keep track of how much we read in the main chunk
550                         new_chunk.bytes_read += temp_chunk.bytes_read
551
552                 #is it an object chunk?
553                 elif (new_chunk.ID == OBJECT):
554                         
555                         if CreateBlenderObject:
556                                 putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials)
557                                 contextMesh_vertls = []; contextMesh_facels = []
558                         
559                                 ## preparando para receber o proximo objeto
560                                 contextMeshMaterials = {} # matname:[face_idxs]
561                                 contextMeshUV = None
562                                 #contextMesh.vertexUV = 1 # Make sticky coords.
563                                 # Reset matrix
564                                 contextMatrix_rot = None
565                                 #contextMatrix_tx = None
566                                 
567                         CreateBlenderObject = True
568                         tempName = read_string(file)
569                         contextObName = tempName
570                         new_chunk.bytes_read += len(tempName)+1
571                 
572                 #is it a material chunk?
573                 elif (new_chunk.ID == MATERIAL):
574
575 #                       print("read material")
576
577                         #print 'elif (new_chunk.ID == MATERIAL):'
578                         contextMaterial = bpy.data.add_material('Material')
579 #                       contextMaterial = bpy.data.materials.new('Material')
580                 
581                 elif (new_chunk.ID == MAT_NAME):
582                         #print 'elif (new_chunk.ID == MAT_NAME):'
583                         material_name = read_string(file)
584
585 #                       print("material name", material_name)
586                         
587                         #plus one for the null character that ended the string
588                         new_chunk.bytes_read += len(material_name)+1
589                         
590                         contextMaterial.name = material_name.rstrip() # remove trailing  whitespace
591                         MATDICT[material_name]= (contextMaterial.name, contextMaterial)
592                 
593                 elif (new_chunk.ID == MAT_AMBIENT):
594                         #print 'elif (new_chunk.ID == MAT_AMBIENT):'
595                         read_chunk(file, temp_chunk)
596                         if (temp_chunk.ID == MAT_FLOAT_COLOR):
597                                 contextMaterial.mirror_color = read_float_color(temp_chunk)
598 #                               temp_data = file.read(struct.calcsize('3f'))
599 #                               temp_chunk.bytes_read += 12
600 #                               contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)]
601                         elif (temp_chunk.ID == MAT_24BIT_COLOR):
602                                 contextMaterial.mirror_color = read_byte_color(temp_chunk)
603 #                               temp_data = file.read(struct.calcsize('3B'))
604 #                               temp_chunk.bytes_read += 3
605 #                               contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb
606                         else:
607                                 skip_to_end(file, temp_chunk)
608                         new_chunk.bytes_read += temp_chunk.bytes_read
609
610                 elif (new_chunk.ID == MAT_DIFFUSE):
611                         #print 'elif (new_chunk.ID == MAT_DIFFUSE):'
612                         read_chunk(file, temp_chunk)
613                         if (temp_chunk.ID == MAT_FLOAT_COLOR):
614                                 contextMaterial.diffuse_color = read_float_color(temp_chunk)
615 #                               temp_data = file.read(struct.calcsize('3f'))
616 #                               temp_chunk.bytes_read += 12
617 #                               contextMaterial.rgbCol = [float(col) for col in struct.unpack('<3f', temp_data)]
618                         elif (temp_chunk.ID == MAT_24BIT_COLOR):
619                                 contextMaterial.diffuse_color = read_byte_color(temp_chunk)
620 #                               temp_data = file.read(struct.calcsize('3B'))
621 #                               temp_chunk.bytes_read += 3
622 #                               contextMaterial.rgbCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb
623                         else:
624                                 skip_to_end(file, temp_chunk)
625
626 #                       print("read material diffuse color", contextMaterial.diffuse_color)
627
628                         new_chunk.bytes_read += temp_chunk.bytes_read
629
630                 elif (new_chunk.ID == MAT_SPECULAR):
631                         #print 'elif (new_chunk.ID == MAT_SPECULAR):'
632                         read_chunk(file, temp_chunk)
633                         if (temp_chunk.ID == MAT_FLOAT_COLOR):
634                                 contextMaterial.specular_color = read_float_color(temp_chunk)
635 #                               temp_data = file.read(struct.calcsize('3f'))
636 #                               temp_chunk.bytes_read += 12
637 #                               contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)]
638                         elif (temp_chunk.ID == MAT_24BIT_COLOR):
639                                 contextMaterial.specular_color = read_byte_color(temp_chunk)
640 #                               temp_data = file.read(struct.calcsize('3B'))
641 #                               temp_chunk.bytes_read += 3
642 #                               contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb
643                         else:
644                                 skip_to_end(file, temp_chunk)
645                         new_chunk.bytes_read += temp_chunk.bytes_read
646                         
647                 elif (new_chunk.ID == MAT_TEXTURE_MAP):
648                         read_texture(new_chunk, temp_chunk, "Diffuse", "COLOR")
649 #                       #print 'elif (new_chunk.ID==MAT_TEXTURE_MAP):'
650 #                       new_texture= bpy.data.textures.new('Diffuse')
651 #                       new_texture.setType('Image')
652 #                       img = None
653 #                       while (new_chunk.bytes_read<new_chunk.length):
654 #                               #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length
655 #                               read_chunk(file, temp_chunk)
656                                 
657 #                               if (temp_chunk.ID==MAT_MAP_FILENAME):
658 #                                       texture_name=read_string(file)
659 #                                       #img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
660 #                                       img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
661 #                                       new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
662                                         
663 #                               else:
664 #                                       skip_to_end(file, temp_chunk)
665                                 
666 #                               new_chunk.bytes_read+= temp_chunk.bytes_read
667                         
668 #                       #add the map to the material in the right channel
669 #                       if img:
670 #                               add_texture_to_material(img, new_texture, contextMaterial, 'DIFFUSE')
671                         
672                 elif (new_chunk.ID == MAT_SPECULAR_MAP):
673                         read_texture(new_chunk, temp_chunk, "Specular", "SPECULARITY")
674 #                       #print 'elif (new_chunk.ID == MAT_SPECULAR_MAP):'
675 #                       new_texture = bpy.data.textures.new('Specular')
676 #                       new_texture.setType('Image')
677 #                       img = None
678 #                       while (new_chunk.bytes_read < new_chunk.length):
679 #                               read_chunk(file, temp_chunk)
680                                 
681 #                               if (temp_chunk.ID == MAT_MAP_FILENAME):
682 #                                       texture_name = read_string(file)
683 #                                       #img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
684 #                                       img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH)
685 #                                       new_chunk.bytes_read+= (len(texture_name)+1) #plus one for the null character that gets removed
686 #                               else:
687 #                                       skip_to_end(file, temp_chunk)
688                                 
689 #                               new_chunk.bytes_read += temp_chunk.bytes_read
690                                 
691 #                       #add the map to the material in the right channel
692 #                       if img:
693 #                               add_texture_to_material(img, new_texture, contextMaterial, 'SPECULAR')
694         
695                 elif (new_chunk.ID == MAT_OPACITY_MAP):
696                         read_texture(new_chunk, temp_chunk, "Opacity", "ALPHA")
697 #                       #print 'new_texture = Blender.Texture.New('Opacity')'
698 #                       new_texture = bpy.data.textures.new('Opacity')
699 #                       new_texture.setType('Image')
700 #                       img = None
701 #                       while (new_chunk.bytes_read < new_chunk.length):
702 #                               read_chunk(file, temp_chunk)
703                                 
704 #                               if (temp_chunk.ID == MAT_MAP_FILENAME):
705 #                                       texture_name = read_string(file)
706 #                                       #img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
707 #                                       img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH)
708 #                                       new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
709 #                               else:
710 #                                       skip_to_end(file, temp_chunk)
711                                 
712 #                               new_chunk.bytes_read += temp_chunk.bytes_read
713 #                       #add the map to the material in the right channel
714 #                       if img:
715 #                               add_texture_to_material(img, new_texture, contextMaterial, 'OPACITY')
716
717                 elif (new_chunk.ID == MAT_BUMP_MAP):
718                         read_texture(new_chunk, temp_chunk, "Bump", "NORMAL")
719 #                       #print 'elif (new_chunk.ID == MAT_BUMP_MAP):'
720 #                       new_texture = bpy.data.textures.new('Bump')
721 #                       new_texture.setType('Image')
722 #                       img = None
723 #                       while (new_chunk.bytes_read < new_chunk.length):
724 #                               read_chunk(file, temp_chunk)
725                                 
726 #                               if (temp_chunk.ID == MAT_MAP_FILENAME):
727 #                                       texture_name = read_string(file)
728 #                                       #img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
729 #                                       img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH)
730 #                                       new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
731 #                               else:
732 #                                       skip_to_end(file, temp_chunk)
733                                 
734 #                               new_chunk.bytes_read += temp_chunk.bytes_read
735                                 
736 #                       #add the map to the material in the right channel
737 #                       if img:
738 #                               add_texture_to_material(img, new_texture, contextMaterial, 'BUMP')
739                         
740                 elif (new_chunk.ID == MAT_TRANSPARENCY):
741                         #print 'elif (new_chunk.ID == MAT_TRANSPARENCY):'
742                         read_chunk(file, temp_chunk)
743                         temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
744                         
745                         temp_chunk.bytes_read += 2
746                         contextMaterial.alpha = 1-(float(struct.unpack('<H', temp_data)[0])/100)
747                         new_chunk.bytes_read += temp_chunk.bytes_read
748
749
750                 elif (new_chunk.ID == OBJECT_LAMP): # Basic lamp support.
751                         
752                         temp_data = file.read(STRUCT_SIZE_3FLOAT)
753                         
754                         x,y,z = struct.unpack('<3f', temp_data)
755                         new_chunk.bytes_read += STRUCT_SIZE_3FLOAT
756
757                         ob = bpy.data.add_object("LAMP", "Lamp")
758                         ob.data = bpy.data.add_lamp("Lamp")
759                         SCN.add_object(ob)
760                         
761                         contextLamp[1]= ob.data
762 #                       contextLamp[1]= bpy.data.lamps.new()
763                         contextLamp[0]= ob
764 #                       contextLamp[0]= SCN_OBJECTS.new(contextLamp[1])
765                         importedObjects.append(contextLamp[0])
766                         
767                         #print 'number of faces: ', num_faces
768                         #print x,y,z
769                         contextLamp[0].location = (x, y, z)
770 #                       contextLamp[0].setLocation(x,y,z)
771                         
772                         # Reset matrix
773                         contextMatrix_rot = None
774                         #contextMatrix_tx = None
775                         #print contextLamp.name, 
776                         
777                 elif (new_chunk.ID == OBJECT_MESH):
778                         # print 'Found an OBJECT_MESH chunk'
779                         pass
780                 elif (new_chunk.ID == OBJECT_VERTICES):
781                         '''
782                         Worldspace vertex locations
783                         '''
784                         # print 'elif (new_chunk.ID == OBJECT_VERTICES):'
785                         temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
786                         num_verts = struct.unpack('<H', temp_data)[0]
787                         new_chunk.bytes_read += 2
788                         
789                         # print 'number of verts: ', num_verts
790                         def getvert():
791                                 temp_data = struct.unpack('<3f', file.read(STRUCT_SIZE_3FLOAT))
792                                 new_chunk.bytes_read += STRUCT_SIZE_3FLOAT #12: 3 floats x 4 bytes each
793                                 return temp_data
794                         
795                         #contextMesh.verts.extend( [Vector(),] ) # DUMMYVERT! - remove when blenders internals are fixed.
796                         contextMesh_vertls = [getvert() for i in range(num_verts)]
797                         
798                         #print 'object verts: bytes read: ', new_chunk.bytes_read
799
800                 elif (new_chunk.ID == OBJECT_FACES):
801                         # print 'elif (new_chunk.ID == OBJECT_FACES):'
802                         temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
803                         num_faces = struct.unpack('<H', temp_data)[0]
804                         new_chunk.bytes_read += 2
805                         #print 'number of faces: ', num_faces
806                         
807                         def getface():
808                                 # print '\ngetting a face'
809                                 temp_data = file.read(STRUCT_SIZE_4UNSIGNED_SHORT)
810                                 new_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each
811                                 v1,v2,v3,dummy = struct.unpack('<4H', temp_data)
812                                 return v1, v2, v3
813                         
814                         contextMesh_facels = [ getface() for i in range(num_faces) ]
815
816
817                 elif (new_chunk.ID == OBJECT_MATERIAL):
818                         # print 'elif (new_chunk.ID == OBJECT_MATERIAL):'
819                         material_name = read_string(file)
820                         new_chunk.bytes_read += len(material_name)+1 # remove 1 null character.
821                         
822                         temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
823                         num_faces_using_mat = struct.unpack('<H', temp_data)[0]
824                         new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
825                         
826                         def getmat():
827                                 temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
828                                 new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
829                                 return struct.unpack('<H', temp_data)[0]
830                         
831                         contextMeshMaterials[material_name]= [ getmat() for i in range(num_faces_using_mat) ]
832                         
833                         #look up the material in all the materials
834
835                 elif (new_chunk.ID == OBJECT_UV):
836                         temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT)
837                         num_uv = struct.unpack('<H', temp_data)[0]
838                         new_chunk.bytes_read += 2
839                         
840                         def getuv():
841                                 temp_data = file.read(STRUCT_SIZE_2FLOAT)
842                                 new_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each
843                                 return Mathutils.Vector( struct.unpack('<2f', temp_data) )
844 #                               return Vector( struct.unpack('<2f', temp_data) )
845                                 
846                         contextMeshUV = [ getuv() for i in range(num_uv) ]
847                 
848                 elif (new_chunk.ID == OBJECT_TRANS_MATRIX):
849                         # How do we know the matrix size? 54 == 4x4 48 == 4x3
850                         temp_data = file.read(STRUCT_SIZE_4x3MAT)
851                         data = list( struct.unpack('<ffffffffffff', temp_data)  )
852                         new_chunk.bytes_read += STRUCT_SIZE_4x3MAT
853                         
854                         contextMatrix_rot = Mathutils.Matrix(\
855 #                       contextMatrix_rot = Blender.Mathutils.Matrix(\
856                          data[:3] + [0],\
857                          data[3:6] + [0],\
858                          data[6:9] + [0],\
859                          data[9:] + [1])
860                         
861                         
862                         '''
863                         contextMatrix_rot = Blender.Mathutils.Matrix(\
864                          data[:3] + [0],\
865                          data[3:6] + [0],\
866                          data[6:9] + [0],\
867                          [0,0,0,1])
868                         '''
869                         
870                         '''
871                         contextMatrix_rot = Blender.Mathutils.Matrix(\
872                          data[:3] ,\
873                          data[3:6],\
874                          data[6:9])
875                         '''
876                         
877                         '''
878                         contextMatrix_rot = Blender.Mathutils.Matrix()
879                         m = 0
880                         for j in xrange(4):
881                                 for i in xrange(3):
882                                         contextMatrix_rot[j][i] = data[m]
883                                         m += 1
884                         
885                         contextMatrix_rot[0][3]=0;
886                         contextMatrix_rot[1][3]=0;
887                         contextMatrix_rot[2][3]=0;
888                         contextMatrix_rot[3][3]=1;
889                         '''
890                         
891                         #contextMatrix_rot.resize4x4()
892                         #print "MTX"
893                         #print contextMatrix_rot
894                         contextMatrix_rot.invert()
895                         #print contextMatrix_rot
896                         #contextMatrix_tx = Blender.Mathutils.TranslationMatrix(0.5 * Blender.Mathutils.Vector(data[9:]))
897                         #contextMatrix_tx.invert()
898                         
899                         #tx.invert()
900                         
901                         #contextMatrix = contextMatrix * tx
902                         #contextMatrix = contextMatrix  *tx
903                         
904                 elif  (new_chunk.ID == MAT_MAP_FILENAME):
905                         texture_name = read_string(file)
906                         try:
907                                 TEXTURE_DICT[contextMaterial.name]
908                         except:
909                                 #img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
910                                 img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname)
911 #                               img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH)
912                         
913                         new_chunk.bytes_read += len(texture_name)+1 #plus one for the null character that gets removed
914                 
915                 else: #(new_chunk.ID!=VERSION or new_chunk.ID!=OBJECTINFO or new_chunk.ID!=OBJECT or new_chunk.ID!=MATERIAL):
916                         # print 'skipping to end of this chunk'
917                         buffer_size = new_chunk.length - new_chunk.bytes_read
918                         binary_format='%ic' % buffer_size
919                         temp_data = file.read(struct.calcsize(binary_format))
920                         new_chunk.bytes_read += buffer_size
921
922
923                 #update the previous chunk bytes read
924                 # print 'previous_chunk.bytes_read += new_chunk.bytes_read'
925                 # print previous_chunk.bytes_read, new_chunk.bytes_read
926                 previous_chunk.bytes_read += new_chunk.bytes_read
927                 ## print 'Bytes left in this chunk: ', previous_chunk.length - previous_chunk.bytes_read
928         
929         # FINISHED LOOP
930         # There will be a number of objects still not added
931         if contextMesh_facels != None:
932                 putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials)
933
934 def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, APPLY_MATRIX=False):
935         global FILENAME, SCN
936 #       global FILENAME, SCN_OBJECTS
937
938         # XXX
939 #       if BPyMessages.Error_NoFile(filename):
940 #               return
941         
942         print('\n\nImporting 3DS: "%s"' % (filename))
943 #       print('\n\nImporting 3DS: "%s"' % (Blender.sys.expandpath(filename)))
944
945         time1 = time.clock()
946 #       time1 = Blender.sys.time()
947         
948         FILENAME = filename
949         current_chunk = chunk()
950         
951         file = open(filename,'rb')
952         
953         #here we go!
954         # print 'reading the first chunk'
955         read_chunk(file, current_chunk)
956         if (current_chunk.ID!=PRIMARY):
957                 print('\tFatal Error:  Not a valid 3ds file: ', filename)
958                 file.close()
959                 return
960         
961         
962         # IMPORT_AS_INSTANCE = Blender.Draw.Create(0)
963 #       IMPORT_CONSTRAIN_BOUNDS = Blender.Draw.Create(10.0)
964 #       IMAGE_SEARCH = Blender.Draw.Create(1)
965 #       APPLY_MATRIX = Blender.Draw.Create(0)
966         
967         # Get USER Options
968 #       pup_block = [\
969 #       ('Size Constraint:', IMPORT_CONSTRAIN_BOUNDS, 0.0, 1000.0, 'Scale the model by 10 until it reacehs the size constraint. Zero Disables.'),\
970 #       ('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\
971 #       ('Transform Fix', APPLY_MATRIX, 'Workaround for object transformations importing incorrectly'),\
972 #       #('Group Instance', IMPORT_AS_INSTANCE, 'Import objects into a new scene and group, creating an instance in the current scene.'),\
973 #       ]
974         
975 #       if PREF_UI:
976 #               if not Blender.Draw.PupBlock('Import 3DS...', pup_block):
977 #                       return
978         
979 #       Blender.Window.WaitCursor(1)
980         
981 #       IMPORT_CONSTRAIN_BOUNDS = IMPORT_CONSTRAIN_BOUNDS.val
982 #       # IMPORT_AS_INSTANCE = IMPORT_AS_INSTANCE.val
983 #       IMAGE_SEARCH = IMAGE_SEARCH.val
984 #       APPLY_MATRIX = APPLY_MATRIX.val
985         
986         if IMPORT_CONSTRAIN_BOUNDS:
987                 BOUNDS_3DS[:]= [1<<30, 1<<30, 1<<30, -1<<30, -1<<30, -1<<30]
988         else:
989                 BOUNDS_3DS[:]= []
990         
991         ##IMAGE_SEARCH
992         
993         scn = context.scene
994 #       scn = bpy.data.scenes.active
995         SCN = scn
996 #       SCN_OBJECTS = scn.objects
997 #       SCN_OBJECTS.selected = [] # de select all
998         
999         importedObjects = [] # Fill this list with objects
1000         process_next_chunk(file, current_chunk, importedObjects, IMAGE_SEARCH)
1001         
1002         
1003         # Link the objects into this scene.
1004         # Layers = scn.Layers
1005         
1006         # REMOVE DUMMYVERT, - remove this in the next release when blenders internal are fixed.
1007         
1008         
1009 #       for ob in importedObjects:
1010 #               if ob.type == 'MESH':
1011 # #             if ob.type=='Mesh':
1012 #                       me = ob.getData(mesh=1)
1013 #                       me.verts.delete([me.verts[0],])
1014 #                       if not APPLY_MATRIX:
1015 #                               me.transform(ob.matrixWorld.copy().invert())
1016         
1017         # Done DUMMYVERT
1018         """
1019         if IMPORT_AS_INSTANCE:
1020                 name = filename.split('\\')[-1].split('/')[-1]
1021                 # Create a group for this import.
1022                 group_scn = Scene.New(name)
1023                 for ob in importedObjects:
1024                         group_scn.link(ob) # dont worry about the layers
1025                 
1026                 grp = Blender.Group.New(name)
1027                 grp.objects = importedObjects
1028                 
1029                 grp_ob = Object.New('Empty', name)
1030                 grp_ob.enableDupGroup = True
1031                 grp_ob.DupGroup = grp
1032                 scn.link(grp_ob)
1033                 grp_ob.Layers = Layers
1034                 grp_ob.sel = 1
1035         else:
1036                 # Select all imported objects.
1037                 for ob in importedObjects:
1038                         scn.link(ob)
1039                         ob.Layers = Layers
1040                         ob.sel = 1
1041         """
1042         
1043         if 0:
1044 #       if IMPORT_CONSTRAIN_BOUNDS!=0.0:
1045                 # Set bounds from objecyt bounding box
1046                 for ob in importedObjects:
1047                         if ob.type == 'MESH':
1048 #                       if ob.type=='Mesh':
1049                                 ob.makeDisplayList() # Why dosnt this update the bounds?
1050                                 for v in ob.getBoundBox():
1051                                         for i in (0,1,2):
1052                                                 if v[i] < BOUNDS_3DS[i]:
1053                                                         BOUNDS_3DS[i]= v[i] # min
1054                                                 
1055                                                 if v[i] > BOUNDS_3DS[i + 3]:
1056                                                         BOUNDS_3DS[i + 3]= v[i] # min
1057                 
1058                 # Get the max axis x/y/z
1059                 max_axis = max(BOUNDS_3DS[3]-BOUNDS_3DS[0], BOUNDS_3DS[4]-BOUNDS_3DS[1], BOUNDS_3DS[5]-BOUNDS_3DS[2])
1060                 # print max_axis
1061                 if max_axis < 1 << 30: # Should never be false but just make sure.
1062                         
1063                         # Get a new scale factor if set as an option
1064                         SCALE = 1.0
1065                         while (max_axis * SCALE) > IMPORT_CONSTRAIN_BOUNDS:
1066                                 SCALE/=10
1067                         
1068                         # SCALE Matrix
1069                         SCALE_MAT = Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1])
1070 #                       SCALE_MAT = Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1])
1071                         
1072                         for ob in importedObjects:
1073                                 ob.setMatrix(ob.matrixWorld * SCALE_MAT)
1074                                 
1075                 # Done constraining to bounds.
1076         
1077         # Select all new objects.
1078         print('finished importing: "%s" in %.4f sec.' % (filename, (time.clock()-time1)))
1079 #       print('finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1)))
1080         file.close()
1081 #       Blender.Window.WaitCursor(0)
1082         
1083
1084 DEBUG = False
1085 # if __name__=='__main__' and not DEBUG:
1086 #       if calcsize == None:
1087 #               Blender.Draw.PupMenu('Error%t|a full python installation not found') 
1088 #       else:
1089 #               Blender.Window.FileSelector(load_3ds, 'Import 3DS', '*.3ds')
1090
1091 # For testing compatibility
1092 #load_3ds('/metavr/convert/vehicle/truck_002/TruckTanker1.3DS', False)
1093 #load_3ds('/metavr/archive/convert/old/arranged_3ds_to_hpx-2/only-need-engine-trains/Engine2.3DS', False)
1094 '''
1095
1096 else:
1097         import os
1098         # DEBUG ONLY
1099         TIME = Blender.sys.time()
1100         import os
1101         print 'Searching for files'
1102         os.system('find /metavr/ -iname "*.3ds" > /tmp/temp3ds_list')
1103         # os.system('find /storage/ -iname "*.3ds" > /tmp/temp3ds_list')
1104         print '...Done'
1105         file = open('/tmp/temp3ds_list', 'r')
1106         lines = file.readlines()
1107         file.close()
1108         # sort by filesize for faster testing
1109         lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines]
1110         lines_size.sort()
1111         lines = [f[1] for f in lines_size]
1112         
1113
1114         def between(v,a,b):
1115                 if v <= max(a,b) and v >= min(a,b):
1116                         return True             
1117                 return False
1118                 
1119         for i, _3ds in enumerate(lines):
1120                 if between(i, 650,800):
1121                         #_3ds= _3ds[:-1]
1122                         print 'Importing', _3ds, '\nNUMBER', i, 'of', len(lines)
1123                         _3ds_file= _3ds.split('/')[-1].split('\\')[-1]
1124                         newScn = Blender.Scene.New(_3ds_file)
1125                         newScn.makeCurrent()
1126                         load_3ds(_3ds, False)
1127
1128         print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME)
1129
1130 '''
1131
1132 class IMPORT_OT_3ds(bpy.types.Operator):
1133         '''
1134         3DS Importer
1135         '''
1136         __idname__ = "import.3ds"
1137         __label__ = 'Import 3DS'
1138         
1139         # List of operator properties, the attributes will be assigned
1140         # to the class instance from the operator settings before calling.
1141
1142         __props__ = [
1143                 bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for importing the 3DS file", maxlen=1024, default= ""),
1144 #               bpy.props.FloatProperty(attr="size_constraint", name="Size Constraint", description="Scale the model by 10 until it reacehs the size constraint. Zero Disables.", min=0.0, max=1000.0, soft_min=0.0, soft_max=1000.0, default=10.0),
1145 #               bpy.props.BoolProperty(attr="search_images", name="Image Search", description="Search subdirectories for any assosiated images (Warning, may be slow)", default=True),
1146 #               bpy.props.BoolProperty(attr="apply_matrix", name="Transform Fix", description="Workaround for object transformations importing incorrectly", default=False),
1147         ]
1148         
1149         def execute(self, context):
1150                 load_3ds(self.filename, context, 0.0, False, False)
1151                 return ('FINISHED',)
1152         
1153         def invoke(self, context, event):
1154                 wm = context.manager
1155                 wm.add_fileselect(self.__operator__)
1156                 return ('RUNNING_MODAL',)
1157         '''
1158         def poll(self, context):
1159                 print("Poll")
1160                 return context.active_object != None'''
1161
1162 bpy.ops.add(IMPORT_OT_3ds)
1163
1164 # NOTES:
1165 # why add 1 extra vertex? and remove it when done?
1166 # disabled scaling to size, this requires exposing bb (easy) and understanding how it works (needs some time)