# ##### 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: Campbell Barton, Bob Holcomb, Richard Lärkäng, Damien McGinnes, Mark Stijnman """ Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen) and using information from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. """ ###################################################### # Data Structures ###################################################### #Some of the chunks that we will export #----- Primary Chunk, at the beginning of each file PRIMARY = 0x4D4D #------ Main Chunks OBJECTINFO = 0x3D3D # This gives the version of the mesh and is found right before the material and object information VERSION = 0x0002 # This gives the version of the .3ds file KFDATA = 0xB000 # 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 = 0xA000 # This holds the material name MATAMBIENT = 0xA010 # Ambient color of the object/material MATDIFFUSE = 0xA020 # This holds the color of the object/material MATSPECULAR = 0xA030 # SPecular color of the object/material MATSHINESS = 0xA040 # ?? MATMAP = 0xA200 # This is a header for a new material MATMAPFILE = 0xA300 # This holds the file name of the texture RGB1 = 0x0011 RGB2 = 0x0012 #>------ sub defines of OBJECT OBJECT_MESH = 0x4100 # This lets us know that we are reading a new object OBJECT_LIGHT = 0x4600 # This lets un know we are reading a light object OBJECT_CAMERA = 0x4700 # This lets un know we are reading a camera object #>------ sub defines of CAMERA OBJECT_CAM_RANGES = 0x4720 # The camera range values #>------ sub defines of OBJECT_MESH OBJECT_VERTICES = 0x4110 # The objects vertices OBJECT_FACES = 0x4120 # The objects faces OBJECT_MATERIAL = 0x4130 # This is found if the object has a material, either texture map or color OBJECT_UV = 0x4140 # The UV texture coordinates OBJECT_TRANS_MATRIX = 0x4160 # The Object Matrix #>------ sub defines of KFDATA KFDATA_KFHDR = 0xB00A KFDATA_KFSEG = 0xB008 KFDATA_KFCURTIME = 0xB009 KFDATA_OBJECT_NODE_TAG = 0xB002 #>------ sub defines of OBJECT_NODE_TAG OBJECT_NODE_ID = 0xB030 OBJECT_NODE_HDR = 0xB010 OBJECT_PIVOT = 0xB013 OBJECT_INSTANCE_NAME = 0xB011 POS_TRACK_TAG = 0xB020 ROT_TRACK_TAG = 0xB021 SCL_TRACK_TAG = 0xB022 import struct # So 3ds max can open files, limit names to 12 in length # this is verry annoying for filenames! name_unique = [] # stores str, ascii only name_mapping = {} # stores {orig: byte} mapping def sane_name(name): name_fixed = name_mapping.get(name) if name_fixed is not None: return name_fixed # strip non ascii chars new_name_clean = new_name = name.encode("ASCII", "replace").decode("ASCII")[:12] i = 0 while new_name in name_unique: new_name = new_name_clean + ".%.3d" % i i += 1 # note, appending the 'str' version. name_unique.append(new_name) name_mapping[name] = new_name = new_name.encode("ASCII", "replace") return new_name def uv_key(uv): return round(uv[0], 6), round(uv[1], 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] mat_name = None if mat is None else mat.name # else there already set to none img = uf.image img_name = None if img is None else img.name 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.material_index >= mat_ls_len: # if f.mat >= mat_ls_len: f.material_index = 0 # f.mat = 0 if free: free_derived_objects(ob) # Make material chunks for all materials used in the meshes: for mat_and_image in materialDict.values(): 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)) ''' if not blender_mesh.users: bpy.data.meshes.remove(blender_mesh) # blender_mesh.vertices = 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(filepath, 'wb') # Recursively write the chunks to file: primary.write(file) # Close the file: file.close() # Clear name mapping vars, could make locals too name_unique[:] = [] name_mapping.clear() # Debugging only: report the exporting time: # Blender.Window.WaitCursor(0) print("3ds export time: %.2f" % (time.clock() - time1)) # Debugging only: dump the chunk hierarchy: #primary.dump() return {'FINISHED'}