#!BPY """ Name: '3D Studio (.3ds)...' Blender: 244 Group: 'Import' Tooltip: 'Import from 3DS file format (.3ds)' """ __author__= ['Bob Holcomb', 'Richard L?rk?ng', 'Damien McGinnes', 'Campbell Barton', 'Mario Lapin'] __url__ = ("blenderartists.org", "www.blender.org", "www.gametutorials.com", "lib3ds.sourceforge.net/") __version__= '0.996' __bpydoc__= '''\ 3ds Importer This script imports a 3ds file and the materials into Blender for editing. Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen). 0.996 by Mario Lapin (mario.lapin@gmail.com) 13/04/200
- Implemented workaround to correct association between name, geometry and materials of imported meshes. Without this patch, version 0.995 of this importer would associate to each mesh object the geometry and the materials of the previously parsed mesh object. By so, the name of the first mesh object would be thrown away, and the name of the last mesh object would be automatically merged with a '.001' at the end. No object would desappear, however object's names and materials would be completely jumbled. 0.995 by Campbell Barton
- workaround for buggy mesh vert delete - minor tweaks 0.99 by Bob Holcomb
- added support for floating point color values that previously broke on import. 0.98 by Campbell Barton
- import faces and verts to lists instead of a mesh, convert to a mesh later - use new index mapping feature of mesh to re-map faces that were not added. 0.97 by Campbell Barton
- Strip material names of spaces - Added import as instance to import the 3ds into its own scene and add a group instance to the current scene - New option to scale down imported objects so they are within a limited bounding area. 0.96 by Campbell Barton
- Added workaround for bug in setting UV's for Zero vert index UV faces. - Removed unique name function, let blender make the names unique. 0.95 by Campbell Barton
- Removed workarounds for Blender 2.41 - Mesh objects split by material- many 3ds objects used more then 16 per mesh. - Removed a lot of unneeded variable creation. 0.94 by Campbell Barton
- Face import tested to be about overall 16x speedup over 0.93. - Material importing speedup. - Tested with more models. - Support some corrupt models. 0.93 by Campbell Barton
- Tested with 400 3ds files from turbosquid and samples. - Tactfully ignore faces that used the same verts twice. - Rollback to 0.83 sloppy un-reorganized code, this broke UV coord loading. - Converted from NMesh to Mesh. - Faster and cleaner new names. - Use external comprehensive image loader. - Re intergrated 0.92 and 0.9 changes - Fixes for 2.41 compat. - Non textured faces do not use a texture flag. 0.92
- Added support for diffuse, alpha, spec, bump maps in a single material 0.9
- Reorganized code into object/material block functions
- Use of Matrix() to copy matrix data
- added support for material transparency
0.83 2005-08-07: Campell Barton - Aggressive image finding and case insensitivy for posisx systems. 0.82a 2005-07-22 - image texture loading (both for face uv and renderer) 0.82 - image texture loading (for face uv) 0.81a (fork- not 0.9) Campbell Barton 2005-06-08 - Simplified import code - Never overwrite data - Faster list handling - Leaves import selected 0.81 Damien McGinnes 2005-01-09 - handle missing images better 0.8 Damien McGinnes 2005-01-08 - copies sticky UV coords to face ones - handles images better - Recommend that you run 'RemoveDoubles' on each imported mesh after using this script ''' # ***** BEGIN GPL LICENSE BLOCK ***** # # Script copyright (C) Bob Holcomb # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- # Importing modules import os import time import struct from import_obj import unpack_face_list, load_image import bpy import Mathutils # import Blender # from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils # from Blender.Mathutils import Vector # import BPyImage # import BPyMessages # try: # from struct import calcsize, unpack # except: # calcsize= unpack= None # # If python version is less than 2.4, try to get set stuff from module # try: # set # except: # from sets import Set as set BOUNDS_3DS = [] #this script imports uvcoords as sticky vertex coords #this parameter enables copying these to face uv coords #which shold be more useful. def createBlenderTexture(material, name, image): texture = bpy.data.textures.new(name) texture.setType('Image') texture.image = image material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) ###################################################### # Data Structures ###################################################### #Some of the chunks that we will see #----- Primary Chunk, at the beginning of each file PRIMARY = int('0x4D4D',16) #------ Main Chunks OBJECTINFO = int('0x3D3D',16); #This gives the version of the mesh and is found right before the material and object information VERSION = int('0x0002',16); #This gives the version of the .3ds file EDITKEYFRAME= int('0xB000',16); #This is the header for all of the key frame info #------ sub defines of OBJECTINFO MATERIAL = 45055 #0xAFFF // This stored the texture info OBJECT = 16384 #0x4000 // This stores the faces, vertices, etc... #>------ sub defines of MATERIAL #------ sub defines of MATERIAL_BLOCK MAT_NAME = int('0xA000',16) # This holds the material name MAT_AMBIENT = int('0xA010',16) # Ambient color of the object/material MAT_DIFFUSE = int('0xA020',16) # This holds the color of the object/material MAT_SPECULAR = int('0xA030',16) # SPecular color of the object/material MAT_SHINESS = int('0xA040',16) # ?? MAT_TRANSPARENCY= int('0xA050',16) # Transparency value of material MAT_SELF_ILLUM = int('0xA080',16) # Self Illumination value of material MAT_WIRE = int('0xA085',16) # Only render's wireframe MAT_TEXTURE_MAP = int('0xA200',16) # This is a header for a new texture map MAT_SPECULAR_MAP= int('0xA204',16) # This is a header for a new specular map MAT_OPACITY_MAP = int('0xA210',16) # This is a header for a new opacity map MAT_REFLECTION_MAP= int('0xA220',16) # This is a header for a new reflection map MAT_BUMP_MAP = int('0xA230',16) # This is a header for a new bump map MAT_MAP_FILENAME = int('0xA300',16) # This holds the file name of the texture MAT_FLOAT_COLOR = int ('0x0010', 16) #color defined as 3 floats MAT_24BIT_COLOR = int ('0x0011', 16) #color defined as 3 bytes #>------ sub defines of OBJECT OBJECT_MESH = int('0x4100',16); # This lets us know that we are reading a new object OBJECT_LAMP = int('0x4600',16); # This lets un know we are reading a light object OBJECT_LAMP_SPOT = int('0x4610',16); # The light is a spotloght. OBJECT_LAMP_OFF = int('0x4620',16); # The light off. OBJECT_LAMP_ATTENUATE = int('0x4625',16); OBJECT_LAMP_RAYSHADE = int('0x4627',16); OBJECT_LAMP_SHADOWED = int('0x4630',16); OBJECT_LAMP_LOCAL_SHADOW = int('0x4640',16); OBJECT_LAMP_LOCAL_SHADOW2 = int('0x4641',16); OBJECT_LAMP_SEE_CONE = int('0x4650',16); OBJECT_LAMP_SPOT_RECTANGULAR = int('0x4651',16); OBJECT_LAMP_SPOT_OVERSHOOT = int('0x4652',16); OBJECT_LAMP_SPOT_PROJECTOR = int('0x4653',16); OBJECT_LAMP_EXCLUDE = int('0x4654',16); OBJECT_LAMP_RANGE = int('0x4655',16); OBJECT_LAMP_ROLL = int('0x4656',16); OBJECT_LAMP_SPOT_ASPECT = int('0x4657',16); OBJECT_LAMP_RAY_BIAS = int('0x4658',16); OBJECT_LAMP_INNER_RANGE = int('0x4659',16); OBJECT_LAMP_OUTER_RANGE = int('0x465A',16); OBJECT_LAMP_MULTIPLIER = int('0x465B',16); OBJECT_LAMP_AMBIENT_LIGHT = int('0x4680',16); OBJECT_CAMERA= int('0x4700',16); # This lets un know we are reading a camera object #>------ sub defines of CAMERA OBJECT_CAM_RANGES= int('0x4720',16); # The camera range values #>------ sub defines of OBJECT_MESH OBJECT_VERTICES = int('0x4110',16); # The objects vertices OBJECT_FACES = int('0x4120',16); # The objects faces OBJECT_MATERIAL = int('0x4130',16); # This is found if the object has a material, either texture map or color OBJECT_UV = int('0x4140',16); # The UV texture coordinates OBJECT_TRANS_MATRIX = int('0x4160',16); # The Object Matrix global scn scn = None #the chunk class class chunk: ID = 0 length = 0 bytes_read = 0 #we don't read in the bytes_read, we compute that binary_format=' 3): print('\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version) #is it an object info chunk? elif (new_chunk.ID == OBJECTINFO): #print 'elif (new_chunk.ID == OBJECTINFO):' # print 'found an OBJECTINFO chunk' process_next_chunk(file, new_chunk, importedObjects, IMAGE_SEARCH) #keep track of how much we read in the main chunk new_chunk.bytes_read += temp_chunk.bytes_read #is it an object chunk? elif (new_chunk.ID == OBJECT): if CreateBlenderObject: putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) contextMesh_vertls = []; contextMesh_facels = [] ## preparando para receber o proximo objeto contextMeshMaterials = {} # matname:[face_idxs] contextMeshUV = None #contextMesh.vertexUV = 1 # Make sticky coords. # Reset matrix contextMatrix_rot = None #contextMatrix_tx = None CreateBlenderObject = True tempName = read_string(file) contextObName = tempName new_chunk.bytes_read += len(tempName)+1 #is it a material chunk? elif (new_chunk.ID == MATERIAL): # print("read material") #print 'elif (new_chunk.ID == MATERIAL):' contextMaterial = bpy.data.add_material('Material') # contextMaterial = bpy.data.materials.new('Material') elif (new_chunk.ID == MAT_NAME): #print 'elif (new_chunk.ID == MAT_NAME):' material_name = read_string(file) # print("material name", material_name) #plus one for the null character that ended the string new_chunk.bytes_read += len(material_name)+1 contextMaterial.name = material_name.rstrip() # remove trailing whitespace MATDICT[material_name]= (contextMaterial.name, contextMaterial) elif (new_chunk.ID == MAT_AMBIENT): #print 'elif (new_chunk.ID == MAT_AMBIENT):' read_chunk(file, temp_chunk) if (temp_chunk.ID == MAT_FLOAT_COLOR): contextMaterial.mirror_color = read_float_color(temp_chunk) # temp_data = file.read(struct.calcsize('3f')) # temp_chunk.bytes_read += 12 # contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] elif (temp_chunk.ID == MAT_24BIT_COLOR): contextMaterial.mirror_color = read_byte_color(temp_chunk) # temp_data = file.read(struct.calcsize('3B')) # temp_chunk.bytes_read += 3 # contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read elif (new_chunk.ID == MAT_DIFFUSE): #print 'elif (new_chunk.ID == MAT_DIFFUSE):' read_chunk(file, temp_chunk) if (temp_chunk.ID == MAT_FLOAT_COLOR): contextMaterial.diffuse_color = read_float_color(temp_chunk) # temp_data = file.read(struct.calcsize('3f')) # temp_chunk.bytes_read += 12 # contextMaterial.rgbCol = [float(col) for col in struct.unpack('<3f', temp_data)] elif (temp_chunk.ID == MAT_24BIT_COLOR): contextMaterial.diffuse_color = read_byte_color(temp_chunk) # temp_data = file.read(struct.calcsize('3B')) # temp_chunk.bytes_read += 3 # contextMaterial.rgbCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) # print("read material diffuse color", contextMaterial.diffuse_color) new_chunk.bytes_read += temp_chunk.bytes_read elif (new_chunk.ID == MAT_SPECULAR): #print 'elif (new_chunk.ID == MAT_SPECULAR):' read_chunk(file, temp_chunk) if (temp_chunk.ID == MAT_FLOAT_COLOR): contextMaterial.specular_color = read_float_color(temp_chunk) # temp_data = file.read(struct.calcsize('3f')) # temp_chunk.bytes_read += 12 # contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] elif (temp_chunk.ID == MAT_24BIT_COLOR): contextMaterial.specular_color = read_byte_color(temp_chunk) # temp_data = file.read(struct.calcsize('3B')) # temp_chunk.bytes_read += 3 # contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb else: skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read elif (new_chunk.ID == MAT_TEXTURE_MAP): read_texture(new_chunk, temp_chunk, "Diffuse", "COLOR") # #print 'elif (new_chunk.ID==MAT_TEXTURE_MAP):' # new_texture= bpy.data.textures.new('Diffuse') # new_texture.setType('Image') # img = None # while (new_chunk.bytes_read BOUNDS_3DS[i + 3]: BOUNDS_3DS[i + 3]= v[i] # min # Get the max axis x/y/z max_axis = max(BOUNDS_3DS[3]-BOUNDS_3DS[0], BOUNDS_3DS[4]-BOUNDS_3DS[1], BOUNDS_3DS[5]-BOUNDS_3DS[2]) # print max_axis if max_axis < 1 << 30: # Should never be false but just make sure. # Get a new scale factor if set as an option SCALE = 1.0 while (max_axis * SCALE) > IMPORT_CONSTRAIN_BOUNDS: SCALE/=10 # SCALE Matrix SCALE_MAT = Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) # SCALE_MAT = Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) for ob in importedObjects: ob.setMatrix(ob.matrixWorld * SCALE_MAT) # Done constraining to bounds. # Select all new objects. print('finished importing: "%s" in %.4f sec.' % (filename, (time.clock()-time1))) # print('finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1))) file.close() # Blender.Window.WaitCursor(0) DEBUG = False # if __name__=='__main__' and not DEBUG: # if calcsize == None: # Blender.Draw.PupMenu('Error%t|a full python installation not found') # else: # Blender.Window.FileSelector(load_3ds, 'Import 3DS', '*.3ds') # For testing compatibility #load_3ds('/metavr/convert/vehicle/truck_002/TruckTanker1.3DS', False) #load_3ds('/metavr/archive/convert/old/arranged_3ds_to_hpx-2/only-need-engine-trains/Engine2.3DS', False) ''' else: import os # DEBUG ONLY TIME = Blender.sys.time() import os print 'Searching for files' os.system('find /metavr/ -iname "*.3ds" > /tmp/temp3ds_list') # os.system('find /storage/ -iname "*.3ds" > /tmp/temp3ds_list') print '...Done' file = open('/tmp/temp3ds_list', 'r') lines = file.readlines() file.close() # sort by filesize for faster testing lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines] lines_size.sort() lines = [f[1] for f in lines_size] def between(v,a,b): if v <= max(a,b) and v >= min(a,b): return True return False for i, _3ds in enumerate(lines): if between(i, 650,800): #_3ds= _3ds[:-1] print 'Importing', _3ds, '\nNUMBER', i, 'of', len(lines) _3ds_file= _3ds.split('/')[-1].split('\\')[-1] newScn = Blender.Scene.New(_3ds_file) newScn.makeCurrent() load_3ds(_3ds, False) print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME) ''' class IMPORT_OT_3ds(bpy.types.Operator): ''' 3DS Importer ''' __idname__ = "import.3ds" __label__ = 'Import 3DS' # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. __props__ = [ bpy.props.StringProperty(attr="path", name="File Path", description="File path used for importing the 3DS file", maxlen= 1024, default= ""), # 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), # bpy.props.BoolProperty(attr="search_images", name="Image Search", description="Search subdirectories for any assosiated images (Warning, may be slow)", default=True), # bpy.props.BoolProperty(attr="apply_matrix", name="Transform Fix", description="Workaround for object transformations importing incorrectly", default=False), ] def execute(self, context): load_3ds(self.path, context, 0.0, False, False) return ('FINISHED',) def invoke(self, context, event): wm = context.manager wm.add_fileselect(self.__operator__) return ('RUNNING_MODAL',) ''' def poll(self, context): print("Poll") return context.active_object != None''' bpy.ops.add(IMPORT_OT_3ds) # NOTES: # why add 1 extra vertex? and remove it when done? # disabled scaling to size, this requires exposing bb (easy) and understanding how it works (needs some time)