# ##### BEGIN GPL LICENSE BLOCK ##### # # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # # Script copyright (C) Bob Holcomb # Contributors: Bob Holcomb, Richard L?rk?ng, Damien McGinnes, Campbell Barton, Mario Lapin import os import time import struct from import_scene_obj import load_image import bpy import mathutils BOUNDS_3DS = [] ###################################################### # 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.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") elif (new_chunk.ID == MAT_SPECULAR_MAP): read_texture(new_chunk, temp_chunk, "Specular", "SPECULARITY") elif (new_chunk.ID == MAT_OPACITY_MAP): read_texture(new_chunk, temp_chunk, "Opacity", "ALPHA") elif (new_chunk.ID == MAT_BUMP_MAP): read_texture(new_chunk, temp_chunk, "Bump", "NORMAL") elif (new_chunk.ID == MAT_TRANSPARENCY): #print 'elif (new_chunk.ID == MAT_TRANSPARENCY):' read_chunk(file, temp_chunk) temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) temp_chunk.bytes_read += 2 contextMaterial.alpha = 1-(float(struct.unpack(' 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.matrix_world = ob.matrix_world * 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() DEBUG = False # 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) ''' from bpy.props import * from io_utils import ImportHelper class IMPORT_OT_autodesk_3ds(bpy.types.Operator, ImportHelper): '''Import from 3DS file format (.3ds)''' bl_idname = "import_scene.autodesk_3ds" bl_label = 'Import 3DS' filename_ext = ".3ds" constrain_size = FloatProperty(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) search_images = BoolProperty(name="Image Search", description="Search subdirectories for any assosiated images (Warning, may be slow)", default=True) apply_transform = BoolProperty(name="Apply Transform", description="Workaround for object transformations importing incorrectly", default=False) def execute(self, context): load_3ds(self.properties.filepath, context, IMPORT_CONSTRAIN_BOUNDS=self.properties.constrain_size, IMAGE_SEARCH=self.properties.search_images, APPLY_MATRIX=self.properties.apply_transform) return {'FINISHED'} def menu_func(self, context): self.layout.operator(IMPORT_OT_autodesk_3ds.bl_idname, text="3D Studio (.3ds)") def register(): bpy.types.INFO_MT_file_import.append(menu_func) def unregister(): bpy.types.INFO_MT_file_import.remove(menu_func) # NOTES: # why add 1 extra vertex? and remove it when done? - "Answer - eekadoodle - would need to re-order UV's without this since face order isnt always what we give blender, BMesh will solve :D" # disabled scaling to size, this requires exposing bb (easy) and understanding how it works (needs some time) if __name__ == "__main__": register()