diff options
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/3ds_export.py | 207 |
1 files changed, 94 insertions, 113 deletions
diff --git a/release/scripts/3ds_export.py b/release/scripts/3ds_export.py index 7742a9d3430..4ffca01828a 100644 --- a/release/scripts/3ds_export.py +++ b/release/scripts/3ds_export.py @@ -48,15 +48,33 @@ from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. import Blender import bpy -from Blender import Object, Material - -import BPyMesh -getMeshFromObject = BPyMesh.getMeshFromObject -import BPyObject -getDerivedObjects= BPyObject.getDerivedObjects +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(name_unique) + name_mapping[name] = new_name + return new_name ###################################################### # Data Structures @@ -117,15 +135,6 @@ POS_TRACK_TAG = long("0xB020",16); ROT_TRACK_TAG = long("0xB021",16); SCL_TRACK_TAG = long("0xB022",16); - -#==============================================# -# Strips the slashes from the back of a string # -#==============================================# -def stripPath(path): - '''Strips the slashes from the back of a string. - ''' - return path.split('/')[-1].split('\\')[-1] - def uv_key(uv): return round(uv.x, 6), round(uv.y, 6) @@ -193,7 +202,6 @@ class _3ds_string(object): def write(self,file): binary_format = "<%ds" % (len(self.value)+1) file.write(struct.pack(binary_format, self.value)) - def __str__(self): return self.value @@ -212,7 +220,9 @@ class _3ds_point_3d(object): def __str__(self): return '(%f, %f, %f)' % (self.x, self.y, self.z) - + +# Used for writing a track +""" class _3ds_point_4d(object): '''Class representing a four-dimensional point for a 3ds file, for instance a quaternion.''' __slots__ = 'x','y','z','w' @@ -228,7 +238,8 @@ class _3ds_point_4d(object): def __str__(self): return '(%f, %f, %f, %f)' % (self.x, self.y, self.z, self.w) - +""" + class _3ds_point_uv(object): '''Class representing a UV-coordinate for a 3ds file.''' __slots__ = 'uv' @@ -442,7 +453,7 @@ def make_material_texture_chunk(id, images): def add_image(img): filename = image.filename.split('\\')[-1].split('/')[-1] mat_sub_file = _3ds_chunk(MATMAPFILE) - mat_sub_file.add_variable("mapfile", _3ds_string(filename)) + mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename))) mat_sub.add_subchunk(mat_sub_file) for image in images: @@ -450,7 +461,7 @@ def make_material_texture_chunk(id, images): return mat_sub -def make_material_chunk(material, image, PREF_TEXTURES): +def make_material_chunk(material, image): '''Make a material chunk out of a blender material.''' material_chunk = _3ds_chunk(MATERIAL) name = _3ds_chunk(MATNAME) @@ -459,20 +470,19 @@ def make_material_chunk(material, image, PREF_TEXTURES): else: name_str = 'None' if image: name_str += image.name - name.add_variable("name", _3ds_string(name_str)) + name.add_variable("name", _3ds_string(sane_name(name_str))) material_chunk.add_subchunk(name) - if material: - material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, [a*material.amb for a in material.rgbCol] )) - material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.rgbCol)) - material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specCol)) - else: + if not material: material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, (0,0,0) )) material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, (.8, .8, .8) )) material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, (1,1,1) )) - # CANT READ IN MAX!!!! SEEMS LIKE THE FILE IS VALID FROM 3DSDUMP :/ - if PREF_TEXTURES: + else: + material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, [a*material.amb for a in material.rgbCol] )) + material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.rgbCol)) + material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specCol)) + images = get_material_images(material) # can be None if image: images.append(image) @@ -494,53 +504,41 @@ class tri_wrapper(object): self.faceuvs= faceuvs self.offset= [0, 0, 0] # offset indicies -def split_into_tri(face, do_uv=False): - '''Split a quad face into two triangles''' - v = face.v - mat = face.mat - if (do_uv): - img = face.image - if img: img = img.name - else: - img = None - - first_tri = tri_wrapper((v[0].index, v[1].index, v[2].index), mat, img) - second_tri = tri_wrapper((v[0].index, v[2].index, v[3].index), mat, img) - - if (do_uv): - uv = face.uv - first_tri.faceuvs= uv_key(uv[0]), uv_key(uv[1]), uv_key(uv[2]) - second_tri.faceuvs= uv_key(uv[0]), uv_key(uv[2]), uv_key(uv[3]) - - - return [first_tri, second_tri] - - def extract_triangles(mesh): '''Extract triangles from a mesh. If the mesh contains quads, they will be split into triangles.''' tri_list = [] do_uv = mesh.faceUV + + if not do_uv: + face_uv = None + img = None - for face in mesh.faces: - num_fv = len(face) - if num_fv==3: - - if (do_uv): - img = face.image - if img: img = img.name - - new_tri = tri_wrapper((face.v[0].index, face.v[1].index, face.v[2].index), face.mat, img) - - if (do_uv): - new_tri.faceuvs= uv_key(face.uv[0]), uv_key(face.uv[1]), uv_key(face.uv[2]) - - tri_list.append(new_tri) - - else: #it's a quad - tri_list.extend( split_into_tri(face, do_uv) ) + for face in mesh.faces: + f_v = face.v + + if do_uv: + f_uv = face.uv + img = face.image + if img: img = img.name + + if len(f_v)==3: + new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img) + if (do_uv): new_tri.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2]) + tri_list.append(new_tri) + + else: #it's a quad + new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img) + new_tri_2 = tri_wrapper((f_v[0].index, f_v[2].index, f_v[3].index), face.mat, img) + + if (do_uv): + new_tri.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2]) + new_tri_2.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[2]), uv_key(f_uv[3]) + + tri_list.append( new_tri ) + tri_list.append( new_tri_2 ) return tri_list @@ -561,21 +559,18 @@ def remove_face_uv(verts, tri_list): for i in xrange(3): # store the index into the UniqueList for future reference: # offset.append(uv_list[tri.vertex_index[i]].add(_3ds_point_uv(tri.faceuvs[i]))) + context_uv_vert= unique_uvs[tri.vertex_index[i]] uvkey= tri.faceuvs[i] - try: - offset_index, uv_3ds= context_uv_vert[uvkey] - except: - offset_index= len(context_uv_vert) - context_uv_vert[tri.faceuvs[i]]= offset_index, _3ds_point_uv(uvkey) + offset_index__uv_3ds = context_uv_vert.get(uvkey) - # No optimizing the array!!! buggy atm - # offset_index= len(context_uv_vert) - # context_uv_vert[tri.faceuvs[i]]= offset_index, _3ds_point_uv(uvkey) + if not offset_index__uv_3ds: + offset_index__uv_3ds = context_uv_vert[uvkey] = len(context_uv_vert), _3ds_point_uv(uvkey) + + tri.offset[i] = offset_index__uv_3ds[0] - tri.offset[i]= offset_index # At this point, each vertex has a UniqueList containing every uv coordinate that is associated with it # only once. @@ -650,7 +645,7 @@ def make_faces_chunk(tri_list, mesh, materialDict): if img: name_str += img context_mat_face_array = _3ds_array() - unique_mats[mat, img] = _3ds_string(name_str), context_mat_face_array + unique_mats[mat, img] = _3ds_string(sane_name(name_str)), context_mat_face_array context_mat_face_array.add(_3ds_short(i)) @@ -669,7 +664,7 @@ def make_faces_chunk(tri_list, mesh, materialDict): obj_material_names=[] for m in materials: if m: - obj_material_names.append(_3ds_string(m.name)) + obj_material_names.append(_3ds_string(sane_name(m.name))) obj_material_faces.append(_3ds_array()) n_materials = len(obj_material_names) @@ -685,7 +680,6 @@ def make_faces_chunk(tri_list, mesh, materialDict): obj_material_chunk.add_variable("face_list", obj_material_faces[i]) face_chunk.add_subchunk(obj_material_chunk) - # asas return face_chunk def make_vert_chunk(vert_array): @@ -763,6 +757,7 @@ def make_kfdata(start=0, stop=0, curtime=0): return kfdata """ +""" def make_track_chunk(ID, obj): '''Make a chunk for track data. @@ -801,6 +796,8 @@ def make_track_chunk(ID, obj): return track_chunk """ + +""" def make_kf_obj_node(obj, name_to_id): '''Make a node chunk for a Blender object. @@ -824,7 +821,7 @@ def make_kf_obj_node(obj, name_to_id): obj_node_header_chunk.add_variable("name", _3ds_string("$$$DUMMY")) else: # Add the name: - obj_node_header_chunk.add_variable("name", _3ds_string(name)) + obj_node_header_chunk.add_variable("name", _3ds_string(sane_name(name))) # Add Flag variables (not sure what they do): obj_node_header_chunk.add_variable("flags1", _3ds_short(0)) obj_node_header_chunk.add_variable("flags2", _3ds_short(0)) @@ -851,7 +848,7 @@ def make_kf_obj_node(obj, name_to_id): # Empty objects need to have an extra chunk for the instance name: if obj.type == 'Empty': obj_instance_name_chunk = _3ds_chunk(OBJECT_INSTANCE_NAME) - obj_instance_name_chunk.add_variable("name", _3ds_string(name)) + obj_instance_name_chunk.add_variable("name", _3ds_string(sane_name(name))) kf_obj_node.add_subchunk(obj_instance_name_chunk) # Add track chunks for position, rotation and scale: @@ -873,12 +870,9 @@ def save_3ds(filename): if not BPyMessages.Warning_SaveOver(filename): return - PREF_TEXTURES = Blender.Draw.PupMenu('Texture (Breaks some importers)%t| YES %x1 | NO %x0') - if PREF_TEXTURES ==-1: return - time1= Blender.sys.time() Blender.Window.WaitCursor(1) - scn= bpy.data.scenes.active + sce= bpy.data.scenes.active # Initialize the main chunk (primary): primary = _3ds_chunk(PRIMARY) @@ -896,19 +890,19 @@ def save_3ds(filename): ''' # Get all the supported objects selected in this scene: - # ob_sel= list(scn.objects.context) - # mesh_objects = [ (ob, me) for ob in ob_sel for me in (BPyMesh.getMeshFromObject(ob, None, True, False, scn),) if me ] + # ob_sel= list(sce.objects.context) + # mesh_objects = [ (ob, me) for ob in ob_sel for me in (BPyMesh.getMeshFromObject(ob, None, True, False, sce),) if me ] # empty_objects = [ ob for ob in ob_sel if ob.type == 'Empty' ] # Make a list of all materials used in the selected meshes (use a dictionary, # each material is added once): materialDict = {} mesh_objects = [] - for ob in scn.objects.context: + for ob in sce.objects.context: for ob_derived, mat in getDerivedObjects(ob, False): - data = getMeshFromObject(ob_derived, None, True, False, scn) + data = getMeshFromObject(ob_derived, None, True, False, sce) if data: - data.transform(mat) + data.transform(mat, recalc_normals=False) mesh_objects.append((ob_derived, data)) mat_ls = data.materials mat_ls_len = len(mat_ls) @@ -922,7 +916,7 @@ def save_3ds(filename): mat_index = f.mat if mat_index >= mat_ls_len: mat_index = f.mat = 0 - mat = mat_ls[f.mat] + mat = mat_ls[mat_index] if mat: mat_name = mat.name else: mat_name = None # else there alredy set to none @@ -930,21 +924,14 @@ def save_3ds(filename): img = f.image if img: img_name = img.name else: img_name = None - - - try: - materialDict[mat_name, img_name] - except: - materialDict[mat_name, img_name]= mat, img + materialDict.setdefault((mat_name, img_name), (mat, img) ) + else: for mat in mat_ls: if mat: # material may be None so check its not. - try: - materialDict[mat.name, None] - except: - materialDict[mat.name, None]= mat, None + materialDict.setdefault((mat.name, None), (mat, None) ) # Why 0 Why! for f in data.faces: @@ -953,7 +940,7 @@ def save_3ds(filename): # 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], PREF_TEXTURES)) + 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: """ @@ -971,7 +958,7 @@ def save_3ds(filename): object_chunk = _3ds_chunk(OBJECT) # set the object name - object_chunk.add_variable("name", _3ds_string(str(i) + ob.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)) @@ -1005,7 +992,7 @@ def save_3ds(filename): # Check the size: primary.get_size() # Open the file for writing: - file = open( filename, "wb" ) + file = open( filename, 'wb' ) # Recursively write the chunks to file: primary.write(file) @@ -1013,20 +1000,14 @@ def save_3ds(filename): # Close the file: file.close() - # Free memory - """ - for ob, blender_mesh in mesh_objects: - blender_mesh.verts= None - """ - # 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') |