#!BPY # coding: utf-8 """ Name: '3D Studio (.3ds)...' Blender: 243 Group: 'Export' Tooltip: 'Export to 3DS file format (.3ds).' """ __author__ = ["Campbell Barton", "Bob Holcomb", "Richard Lärkäng", "Damien McGinnes", "Mark Stijnman"] __url__ = ("blenderartists.org", "www.blender.org", "www.gametutorials.com", "lib3ds.sourceforge.net/") __version__ = "0.90a" __bpydoc__ = """\ 3ds Exporter This script Exports a 3ds file. Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen) and using information from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. """ # ***** 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 Blender import bpy from BPyMesh import getMeshFromObject from BPyObject import getDerivedObjects import struct # So 3ds max can open files, limit names to 12 in length # this is verry annoying for filenames! name_unique = [] name_mapping = {} def sane_name(name): name_fixed = name_mapping.get(name) if name_fixed != None: return name_fixed if len(name) > 12: new_name = name[:12] else: new_name = name i = 0 while new_name in name_unique: new_name = new_name[:-4] + '.%.3d' % i i+=1 name_unique.append(new_name) name_mapping[name] = new_name return new_name ###################################################### # Data Structures ###################################################### #Some of the chunks that we will export #----- Primary Chunk, at the beginning of each file PRIMARY= long("0x4D4D",16) #------ Main Chunks OBJECTINFO = long("0x3D3D",16); #This gives the version of the mesh and is found right before the material and object information VERSION = long("0x0002",16); #This gives the version of the .3ds file KFDATA = long("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 MATNAME = long("0xA000",16); # This holds the material name MATAMBIENT = long("0xA010",16); # Ambient color of the object/material MATDIFFUSE = long("0xA020",16); # This holds the color of the object/material MATSPECULAR = long("0xA030",16); # SPecular color of the object/material MATSHINESS = long("0xA040",16); # ?? MATMAP = long("0xA200",16); # This is a header for a new material MATMAPFILE = long("0xA300",16); # This holds the file name of the texture RGB1= long("0x0011",16) RGB2= long("0x0012",16) #>------ sub defines of OBJECT OBJECT_MESH = long("0x4100",16); # This lets us know that we are reading a new object OBJECT_LIGHT = long("0x4600",16); # This lets un know we are reading a light object OBJECT_CAMERA= long("0x4700",16); # This lets un know we are reading a camera object #>------ sub defines of CAMERA OBJECT_CAM_RANGES= long("0x4720",16); # The camera range values #>------ sub defines of OBJECT_MESH OBJECT_VERTICES = long("0x4110",16); # The objects vertices OBJECT_FACES = long("0x4120",16); # The objects faces OBJECT_MATERIAL = long("0x4130",16); # This is found if the object has a material, either texture map or color OBJECT_UV = long("0x4140",16); # The UV texture coordinates OBJECT_TRANS_MATRIX = long("0x4160",16); # The Object Matrix #>------ sub defines of KFDATA KFDATA_KFHDR = long("0xB00A",16); KFDATA_KFSEG = long("0xB008",16); KFDATA_KFCURTIME = long("0xB009",16); KFDATA_OBJECT_NODE_TAG = long("0xB002",16); #>------ sub defines of OBJECT_NODE_TAG OBJECT_NODE_ID = long("0xB030",16); OBJECT_NODE_HDR = long("0xB010",16); OBJECT_PIVOT = long("0xB013",16); OBJECT_INSTANCE_NAME = long("0xB011",16); POS_TRACK_TAG = long("0xB020",16); ROT_TRACK_TAG = long("0xB021",16); SCL_TRACK_TAG = long("0xB022",16); def uv_key(uv): return round(uv.x, 6), round(uv.y, 6) # size defines: SZ_SHORT = 2 SZ_INT = 4 SZ_FLOAT = 4 class _3ds_short(object): '''Class representing a short (2-byte integer) for a 3ds file. *** This looks like an unsigned short H is unsigned from the struct docs - Cam***''' __slots__ = 'value' def __init__(self, val=0): self.value=val def get_size(self): return SZ_SHORT def write(self,file): file.write(struct.pack("= mat_ls_len: mat_index = f.mat = 0 mat = mat_ls[mat_index] if mat: mat_name = mat.name else: mat_name = None # else there alredy set to none img = f.image if img: img_name = img.name else: img_name = None materialDict.setdefault((mat_name, img_name), (mat, img) ) else: for mat in mat_ls: if mat: # material may be None so check its not. materialDict.setdefault((mat.name, None), (mat, None) ) # Why 0 Why! for f in data.faces: if f.mat >= mat_ls_len: f.mat = 0 # Make material chunks for all materials used in the meshes: for mat_and_image in materialDict.itervalues(): object_info.add_subchunk(make_material_chunk(mat_and_image[0], mat_and_image[1])) # Give all objects a unique ID and build a dictionary from object name to object id: """ name_to_id = {} for ob, data in mesh_objects: name_to_id[ob.name]= len(name_to_id) #for ob in empty_objects: # name_to_id[ob.name]= len(name_to_id) """ # Create object chunks for all meshes: i = 0 for ob, blender_mesh in mesh_objects: # create a new object chunk object_chunk = _3ds_chunk(OBJECT) # set the object name object_chunk.add_variable("name", _3ds_string(sane_name(ob.name))) # make a mesh chunk out of the mesh: object_chunk.add_subchunk(make_mesh_chunk(blender_mesh, materialDict)) object_info.add_subchunk(object_chunk) ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX # make a kf object node for the object: kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id)) ''' blender_mesh.verts = None i+=i # Create chunks for all empties: ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX for ob in empty_objects: # Empties only require a kf object node: kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id)) pass ''' # Add main object info chunk to primary chunk: primary.add_subchunk(object_info) ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX # Add main keyframe data chunk to primary chunk: primary.add_subchunk(kfdata) ''' # At this point, the chunk hierarchy is completely built. # Check the size: primary.get_size() # Open the file for writing: file = open( filename, 'wb' ) # Recursively write the chunks to file: primary.write(file) # Close the file: file.close() # Debugging only: report the exporting time: Blender.Window.WaitCursor(0) print "3ds export time: %.2f" % (Blender.sys.time() - time1) # Debugging only: dump the chunk hierarchy: #primary.dump() if __name__=='__main__': Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds')) # save_3ds('/test_b.3ds')