diff options
-rw-r--r-- | release/scripts/io/export_3ds.py | 323 | ||||
-rw-r--r-- | release/scripts/io/export_fbx.py | 967 | ||||
-rw-r--r-- | release/scripts/io/export_obj.py | 959 | ||||
-rw-r--r-- | release/scripts/io/export_ply.py | 231 | ||||
-rw-r--r-- | release/scripts/io/export_x3d.py | 553 | ||||
-rw-r--r-- | release/scripts/io/import_3ds.py | 1068 | ||||
-rw-r--r-- | release/scripts/io/import_obj.py | 752 |
7 files changed, 3100 insertions, 1753 deletions
diff --git a/release/scripts/io/export_3ds.py b/release/scripts/io/export_3ds.py index 87680bce1b0..2c1999c3d45 100644 --- a/release/scripts/io/export_3ds.py +++ b/release/scripts/io/export_3ds.py @@ -46,14 +46,35 @@ from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. # Importing modules ###################################################### -import Blender +import struct +import os +import time + import bpy -from BPyMesh import getMeshFromObject -from BPyObject import getDerivedObjects -try: - import struct -except: - struct = None + +# import Blender +# from BPyMesh import getMeshFromObject +# from BPyObject import getDerivedObjects +# try: +# import struct +# except: +# struct = None + +# also used by X3D exporter +# return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects() +def create_derived_objects(ob): + if ob.parent and ob.parent.dupli_type != 'NONE': + return False, None + + if ob.dupli_type != 'NONE': + ob.create_dupli_list() + return True, [(dob.object, dob.matrix) for dob in ob.dupli_list] + else: + return False, [(ob, ob.matrix)] + +# also used by X3D exporter +def free_derived_objects(ob): + ob.free_dupli_list() # So 3ds max can open files, limit names to 12 in length # this is verry annoying for filenames! @@ -85,61 +106,62 @@ def sane_name(name): #Some of the chunks that we will export #----- Primary Chunk, at the beginning of each file -PRIMARY= long("0x4D4D",16) +PRIMARY= int("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 +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 +KFDATA = 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 -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 +MATNAME = int("0xA000",16); # This holds the material name +MATAMBIENT = int("0xA010",16); # Ambient color of the object/material +MATDIFFUSE = int("0xA020",16); # This holds the color of the object/material +MATSPECULAR = int("0xA030",16); # SPecular color of the object/material +MATSHINESS = int("0xA040",16); # ?? +MATMAP = int("0xA200",16); # This is a header for a new material +MATMAPFILE = int("0xA300",16); # This holds the file name of the texture -RGB1= long("0x0011",16) -RGB2= long("0x0012",16) +RGB1= int("0x0011",16) +RGB2= int("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 +OBJECT_MESH = int("0x4100",16); # This lets us know that we are reading a new object +OBJECT_LIGHT = int("0x4600",16); # This lets un know we are reading a light object +OBJECT_CAMERA= int("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 +OBJECT_CAM_RANGES= int("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 +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 #>------ 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); +KFDATA_KFHDR = int("0xB00A",16); +KFDATA_KFSEG = int("0xB008",16); +KFDATA_KFCURTIME = int("0xB009",16); +KFDATA_OBJECT_NODE_TAG = int("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); +OBJECT_NODE_ID = int("0xB030",16); +OBJECT_NODE_HDR = int("0xB010",16); +OBJECT_PIVOT = int("0xB013",16); +OBJECT_INSTANCE_NAME = int("0xB011",16); +POS_TRACK_TAG = int("0xB020",16); +ROT_TRACK_TAG = int("0xB021",16); +SCL_TRACK_TAG = int("0xB022",16); def uv_key(uv): - return round(uv.x, 6), round(uv.y, 6) + return round(uv[0], 6), round(uv[1], 6) +# return round(uv.x, 6), round(uv.y, 6) # size defines: SZ_SHORT = 2 @@ -272,7 +294,8 @@ class _3ds_rgb_color(object): return 3 def write(self,file): - file.write( struct.pack('<3c', chr(int(255*self.r)), chr(int(255*self.g)), chr(int(255*self.b)) ) ) + file.write( struct.pack('<3B', int(255*self.r), int(255*self.g), int(255*self.b) ) ) +# file.write( struct.pack('<3c', chr(int(255*self.r)), chr(int(255*self.g)), chr(int(255*self.b)) ) ) def __str__(self): return '{%f, %f, %f}' % (self.r, self.g, self.b) @@ -343,12 +366,12 @@ class _3ds_named_variable(object): def dump(self,indent): if (self.value!=None): spaces="" - for i in xrange(indent): + for i in range(indent): spaces+=" "; if (self.name!=""): - print spaces, self.name, " = ", self.value + print(spaces, self.name, " = ", self.value) else: - print spaces, "[unnamed]", " = ", self.value + print(spaces, "[unnamed]", " = ", self.value) #the chunk class @@ -408,9 +431,9 @@ class _3ds_chunk(object): Dump is used for debugging purposes, to dump the contents of a chunk to the standard output. Uses the dump function of the named variables and the subchunks to do the actual work.''' spaces="" - for i in xrange(indent): + for i in range(indent): spaces+=" "; - print spaces, "ID=", hex(self.ID.value), "size=", self.get_size() + print(spaces, "ID=", hex(self.ID.value), "size=", self.get_size()) for variable in self.variables: variable.dump(indent+1) for subchunk in self.subchunks: @@ -424,14 +447,19 @@ class _3ds_chunk(object): def get_material_images(material): # blender utility func. - images = [] if material: - for mtex in material.getTextures(): - if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: - image = mtex.tex.image - if image: - images.append(image) # maye want to include info like diffuse, spec here. - return images + return [s.texture.image for s in material.textures if s and s.texture.type == 'IMAGE' and s.texture.image] + + return [] +# images = [] +# if material: +# for mtex in material.getTextures(): +# if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: +# image = mtex.tex.image +# if image: +# images.append(image) # maye want to include info like diffuse, spec here. +# return images + def make_material_subchunk(id, color): '''Make a material subchunk. @@ -454,7 +482,8 @@ def make_material_texture_chunk(id, images): mat_sub = _3ds_chunk(id) def add_image(img): - filename = image.filename.split('\\')[-1].split('/')[-1] + filename = os.path.basename(image.filename) +# filename = image.filename.split('\\')[-1].split('/')[-1] mat_sub_file = _3ds_chunk(MATMAPFILE) mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename))) mat_sub.add_subchunk(mat_sub_file) @@ -482,9 +511,12 @@ def make_material_chunk(material, image): material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, (1,1,1) )) 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)) + material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, [a*material.ambient for a in material.diffuse_color] )) +# 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.diffuse_color)) +# material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.rgbCol)) + material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specular_color)) +# material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specCol)) images = get_material_images(material) # can be None if image: images.append(image) @@ -513,28 +545,39 @@ def extract_triangles(mesh): If the mesh contains quads, they will be split into triangles.''' tri_list = [] - do_uv = mesh.faceUV + do_uv = len(mesh.uv_textures) +# do_uv = mesh.faceUV - if not do_uv: - face_uv = None +# if not do_uv: +# face_uv = None img = None - for face in mesh.faces: - f_v = face.v + for i, face in enumerate(mesh.faces): + f_v = face.verts +# f_v = face.v + + uf = mesh.active_uv_texture.data[i] if do_uv else None if do_uv: - f_uv = face.uv - img = face.image + f_uv = uf.uv + # f_uv = (uf.uv1, uf.uv2, uf.uv3, uf.uv4) if face.verts[3] else (uf.uv1, uf.uv2, uf.uv3) +# f_uv = face.uv + img = uf.image if uf else None +# img = face.image if img: img = img.name + # if f_v[3] == 0: if len(f_v)==3: - new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img) + new_tri = tri_wrapper((f_v[0], f_v[1], f_v[2]), face.material_index, img) +# 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) + new_tri = tri_wrapper((f_v[0], f_v[1], f_v[2]), face.material_index, img) +# 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], f_v[2], f_v[3]), face.material_index, 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]) @@ -555,11 +598,11 @@ def remove_face_uv(verts, tri_list): # initialize a list of UniqueLists, one per vertex: #uv_list = [UniqueList() for i in xrange(len(verts))] - unique_uvs= [{} for i in xrange(len(verts))] + unique_uvs= [{} for i in range(len(verts))] # for each face uv coordinate, add it to the UniqueList of the vertex for tri in tri_list: - for i in xrange(3): + for i in range(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]))) @@ -589,7 +632,7 @@ def remove_face_uv(verts, tri_list): pt = _3ds_point_3d(vert.co) # reuse, should be ok uvmap = [None] * len(unique_uvs[i]) - for ii, uv_3ds in unique_uvs[i].itervalues(): + for ii, uv_3ds in unique_uvs[i].values(): # add a vertex duplicate to the vertex_array for every uv associated with this vertex: vert_array.add(pt) # add the uv coordinate to the uv array: @@ -607,7 +650,7 @@ def remove_face_uv(verts, tri_list): # Make sure the triangle vertex indices now refer to the new vertex list: for tri in tri_list: - for i in xrange(3): + for i in range(3): tri.offset[i]+=index_list[tri.vertex_index[i]] tri.vertex_index= tri.offset @@ -626,7 +669,8 @@ def make_faces_chunk(tri_list, mesh, materialDict): face_list = _3ds_array() - if mesh.faceUV: + if len(mesh.uv_textures): +# if mesh.faceUV: # Gather materials used in this mesh - mat/image pairs unique_mats = {} for i,tri in enumerate(tri_list): @@ -655,7 +699,7 @@ def make_faces_chunk(tri_list, mesh, materialDict): # obj_material_faces[tri.mat].add(_3ds_short(i)) face_chunk.add_variable("faces", face_list) - for mat_name, mat_faces in unique_mats.itervalues(): + for mat_name, mat_faces in unique_mats.values(): obj_material_chunk=_3ds_chunk(OBJECT_MATERIAL) obj_material_chunk.add_variable("name", mat_name) obj_material_chunk.add_variable("face_list", mat_faces) @@ -677,7 +721,7 @@ def make_faces_chunk(tri_list, mesh, materialDict): obj_material_faces[tri.mat].add(_3ds_short(i)) face_chunk.add_variable("faces", face_list) - for i in xrange(n_materials): + for i in range(n_materials): obj_material_chunk=_3ds_chunk(OBJECT_MATERIAL) obj_material_chunk.add_variable("name", obj_material_names[i]) obj_material_chunk.add_variable("face_list", obj_material_faces[i]) @@ -703,7 +747,8 @@ def make_mesh_chunk(mesh, materialDict): # Extract the triangles from the mesh: tri_list = extract_triangles(mesh) - if mesh.faceUV: + if len(mesh.uv_textures): +# if mesh.faceUV: # Remove the face UVs and convert it to vertex UV: vert_array, uv_array, tri_list = remove_face_uv(mesh.verts, tri_list) else: @@ -712,10 +757,13 @@ def make_mesh_chunk(mesh, materialDict): for vert in mesh.verts: vert_array.add(_3ds_point_3d(vert.co)) # If the mesh has vertex UVs, create an array of UVs: - if mesh.vertexUV: + if len(mesh.sticky): +# if mesh.vertexUV: uv_array = _3ds_array() - for vert in mesh.verts: - uv_array.add(_3ds_point_uv(vert.uvco)) + for uv in mesh.sticky: +# for vert in mesh.verts: + uv_array.add(_3ds_point_uv(uv.co)) +# uv_array.add(_3ds_point_uv(vert.uvco)) else: # no UV at all: uv_array = None @@ -862,20 +910,25 @@ def make_kf_obj_node(obj, name_to_id): return kf_obj_node """ -import BPyMessages -def save_3ds(filename): +# import BPyMessages +def save_3ds(filename, context): '''Save the Blender scene to a 3ds file.''' # Time the export if not filename.lower().endswith('.3ds'): filename += '.3ds' - if not BPyMessages.Warning_SaveOver(filename): - return + # XXX +# if not BPyMessages.Warning_SaveOver(filename): +# return - time1= Blender.sys.time() - Blender.Window.WaitCursor(1) - sce= bpy.data.scenes.active + # XXX + time1 = time.clock() +# time1= Blender.sys.time() +# Blender.Window.WaitCursor(1) + + sce = context.scene +# sce= bpy.data.scenes.active # Initialize the main chunk (primary): primary = _3ds_chunk(PRIMARY) @@ -901,22 +954,39 @@ def save_3ds(filename): # each material is added once): materialDict = {} mesh_objects = [] - for ob in sce.objects.context: - for ob_derived, mat in getDerivedObjects(ob, False): - data = getMeshFromObject(ob_derived, None, True, False, sce) + for ob in [ob for ob in context.scene.objects if ob.is_visible()]: +# for ob in sce.objects.context: + + # get derived objects + free, derived = create_derived_objects(ob) + + if derived == None: continue + + for ob_derived, mat in derived: +# for ob_derived, mat in getDerivedObjects(ob, False): + + if ob.type not in ('MESH', 'CURVE', 'SURFACE', 'TEXT', 'META'): + continue + + data = ob_derived.create_mesh(True, 'PREVIEW') +# data = getMeshFromObject(ob_derived, None, True, False, sce) if data: - data.transform(mat, recalc_normals=False) + 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) + # get material/image tuples. - if data.faceUV: + if len(data.uv_textures): +# if data.faceUV: if not mat_ls: mat = mat_name = None - for f in data.faces: + for f, uf in zip(data.faces, data.active_uv_texture.data): if mat_ls: - mat_index = f.mat + mat_index = f.material_index +# mat_index = f.mat if mat_index >= mat_ls_len: mat_index = f.mat = 0 mat = mat_ls[mat_index] @@ -924,7 +994,8 @@ def save_3ds(filename): else: mat_name = None # else there alredy set to none - img = f.image + img = uf.image +# img = f.image if img: img_name = img.name else: img_name = None @@ -938,11 +1009,17 @@ def save_3ds(filename): # Why 0 Why! for f in data.faces: - if f.mat >= mat_ls_len: - f.mat = 0 + 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.itervalues(): + 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: @@ -971,7 +1048,10 @@ def save_3ds(filename): # make a kf object node for the object: kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id)) ''' - blender_mesh.verts = None +# if not blender_mesh.users: + bpy.data.remove_mesh(blender_mesh) +# blender_mesh.verts = None + i+=i # Create chunks for all empties: @@ -1004,16 +1084,47 @@ def save_3ds(filename): file.close() # Debugging only: report the exporting time: - Blender.Window.WaitCursor(0) - print "3ds export time: %.2f" % (Blender.sys.time() - time1) +# Blender.Window.WaitCursor(0) + print("3ds export time: %.2f" % (time.clock() - time1)) +# print("3ds export time: %.2f" % (Blender.sys.time() - time1)) # Debugging only: dump the chunk hierarchy: #primary.dump() -if __name__=='__main__': - if struct: - Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds')) - else: - Blender.Draw.PupMenu("Error%t|This script requires a full python installation") -# save_3ds('/test_b.3ds') +# if __name__=='__main__': +# if struct: +# Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds')) +# else: +# Blender.Draw.PupMenu("Error%t|This script requires a full python installation") +# # save_3ds('/test_b.3ds') + +class EXPORT_OT_3ds(bpy.types.Operator): + ''' + 3DS Exporter + ''' + __idname__ = "export.3ds" + __label__ = 'Export 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="filename", name="File Name", description="File name used for exporting the 3DS file", maxlen= 1024, default= ""), + bpy.props.StringProperty(attr="path", name="File Path", description="File path used for exporting the 3DS file", maxlen= 1024, default= ""), + ] + + def execute(self, context): + save_3ds(self.path, context) + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + def poll(self, context): # Poll isnt working yet + print("Poll") + return context.active_object != None + +bpy.ops.add(EXPORT_OT_3ds) diff --git a/release/scripts/io/export_fbx.py b/release/scripts/io/export_fbx.py index 50357cbfa75..21b1388ebfe 100644 --- a/release/scripts/io/export_fbx.py +++ b/release/scripts/io/export_fbx.py @@ -36,11 +36,16 @@ http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- -try: - import time - # import os # only needed for batch export, nbot used yet -except: - time = None # use this to check if they have python modules installed +import os +import time +import math # math.pi +import shutil # for file copying + +# try: +# import time +# # import os # only needed for batch export, nbot used yet +# except: +# time = None # use this to check if they have python modules installed # for python 2.3 support try: @@ -51,20 +56,21 @@ except: except: set = None # so it complains you dont have a ! -# os is only needed for batch 'own dir' option -try: - import os -except: - os = None +# # os is only needed for batch 'own dir' option +# try: +# import os +# except: +# os = None -import Blender +# import Blender import bpy -from Blender.Mathutils import Matrix, Vector, RotationMatrix +import Mathutils +# from Blender.Mathutils import Matrix, Vector, RotationMatrix -import BPyObject -import BPyMesh -import BPySys -import BPyMessages +# import BPyObject +# import BPyMesh +# import BPySys +# import BPyMessages ## This was used to make V, but faster not to do all that ##valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_,.()[]{}' @@ -75,7 +81,7 @@ invalid = ''.join([chr(i) for i in v]) def cleanName(name): for ch in invalid: name = name.replace(ch, '_') return name -del v, i +# del v, i def copy_file(source, dest): @@ -88,9 +94,10 @@ def copy_file(source, dest): file.close() +# XXX not used anymore, images are copied one at a time def copy_images(dest_dir, textures): - if not dest_dir.endswith(Blender.sys.sep): - dest_dir += Blender.sys.sep + if not dest_dir.endswith(os.sep): + dest_dir += os.sep image_paths = set() for tex in textures: @@ -103,19 +110,30 @@ def copy_images(dest_dir, textures): # Make a name for the target path. dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] if not Blender.sys.exists(dest_image_path): # Image isnt alredy there - print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) + print('\tCopying "%s" > "%s"' % (image_path, dest_image_path)) try: copy_file(image_path, dest_image_path) copyCount+=1 except: - print '\t\tWarning, file failed to copy, skipping.' + print('\t\tWarning, file failed to copy, skipping.') - print '\tCopied %d images' % copyCount + print('\tCopied %d images' % copyCount) + +# I guess FBX uses degrees instead of radians (Arystan). +# Call this function just before writing to FBX. +def eulerRadToDeg(eul): + ret = Mathutils.Euler() -mtx4_identity = Matrix() + ret.x = 180 / math.pi * eul[0] + ret.y = 180 / math.pi * eul[1] + ret.z = 180 / math.pi * eul[2] + + return ret + +mtx4_identity = Mathutils.Matrix() # testing -mtx_x90 = RotationMatrix( 90, 3, 'x') # used +mtx_x90 = Mathutils.RotationMatrix( math.pi/2, 3, 'x') # used #mtx_x90n = RotationMatrix(-90, 3, 'x') #mtx_y90 = RotationMatrix( 90, 3, 'y') #mtx_y90n = RotationMatrix(-90, 3, 'y') @@ -123,14 +141,14 @@ mtx_x90 = RotationMatrix( 90, 3, 'x') # used #mtx_z90n = RotationMatrix(-90, 3, 'z') #mtx4_x90 = RotationMatrix( 90, 4, 'x') -mtx4_x90n = RotationMatrix(-90, 4, 'x') # used +mtx4_x90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'x') # used #mtx4_y90 = RotationMatrix( 90, 4, 'y') -mtx4_y90n = RotationMatrix(-90, 4, 'y') # used -mtx4_z90 = RotationMatrix( 90, 4, 'z') # used -mtx4_z90n = RotationMatrix(-90, 4, 'z') # used +mtx4_y90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'y') # used +mtx4_z90 = Mathutils.RotationMatrix( math.pi/2, 4, 'z') # used +mtx4_z90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'z') # used -def strip_path(p): - return p.split('\\')[-1].split('/')[-1] +# def strip_path(p): +# return p.split('\\')[-1].split('/')[-1] # Used to add the scene name into the filename without using odd chars sane_name_mapping_ob = {} @@ -186,7 +204,7 @@ def sane_name(data, dct): #name = BPySys.cleanName(name) name = cleanName(name) # use our own - while name in dct.itervalues(): name = increment_string(name) + while name in iter(dct.values()): name = increment_string(name) if use_other: # even if other is None - orig_name_other will be a string or None dct[orig_name, orig_name_other] = name @@ -201,27 +219,70 @@ def sane_texname(data): return sane_name(data, sane_name_mapping_tex) def sane_takename(data): return sane_name(data, sane_name_mapping_take) def sane_groupname(data): return sane_name(data, sane_name_mapping_group) -def derived_paths(fname_orig, basepath, FORCE_CWD=False): - ''' - fname_orig - blender path, can be relative - basepath - fname_rel will be relative to this - FORCE_CWD - dont use the basepath, just add a ./ to the filename. - use when we know the file will be in the basepath. - ''' - fname = Blender.sys.expandpath(fname_orig) - fname_strip = strip_path(fname) - if FORCE_CWD: fname_rel = '.' + Blender.sys.sep + fname_strip - else: fname_rel = Blender.sys.relpath(fname, basepath) - if fname_rel.startswith('//'): fname_rel = '.' + Blender.sys.sep + fname_rel[2:] - return fname, fname_strip, fname_rel +# def derived_paths(fname_orig, basepath, FORCE_CWD=False): +# ''' +# fname_orig - blender path, can be relative +# basepath - fname_rel will be relative to this +# FORCE_CWD - dont use the basepath, just add a ./ to the filename. +# use when we know the file will be in the basepath. +# ''' +# fname = bpy.sys.expandpath(fname_orig) +# # fname = Blender.sys.expandpath(fname_orig) +# fname_strip = os.path.basename(fname) +# # fname_strip = strip_path(fname) +# if FORCE_CWD: +# fname_rel = '.' + os.sep + fname_strip +# else: +# fname_rel = bpy.sys.relpath(fname, basepath) +# # fname_rel = Blender.sys.relpath(fname, basepath) +# if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:] +# return fname, fname_strip, fname_rel def mat4x4str(mat): return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([ f for v in mat for f in v ]) +# XXX not used +# duplicated in OBJ exporter +def getVertsFromGroup(me, group_index): + ret = [] + + for i, v in enumerate(me.verts): + for g in v.groups: + if g.group == group_index: + ret.append((i, g.weight)) + + return ret + +# ob must be OB_MESH +def BPyMesh_meshWeight2List(ob): + ''' Takes a mesh and return its group names and a list of lists, one list per vertex. + aligning the each vert list with the group names, each list contains float value for the weight. + These 2 lists can be modified and then used with list2MeshWeight to apply the changes. + ''' + + me = ob.data + + # Clear the vert group. + groupNames= [g.name for g in ob.vertex_groups] + len_groupNames= len(groupNames) + + if not len_groupNames: + # no verts? return a vert aligned empty list + return [[] for i in range(len(me.verts))], [] + else: + vWeightList= [[0.0]*len_groupNames for i in range(len(me.verts))] + + for i, v in enumerate(me.verts): + for g in v.groups: + vWeightList[i][g.group] = g.weight + + return groupNames, vWeightList + def meshNormalizedWeights(me): try: # account for old bad BPyMesh - groupNames, vWeightList = BPyMesh.meshWeight2List(me) + groupNames, vWeightList = BPyMesh_meshWeight2List(me) +# groupNames, vWeightList = BPyMesh.meshWeight2List(me) except: return [],[] @@ -249,23 +310,23 @@ header_comment = \ # This func can be called with just the filename def write(filename, batch_objects = None, \ + context = None, EXP_OBS_SELECTED = True, EXP_MESH = True, EXP_MESH_APPLY_MOD = True, - EXP_MESH_HQ_NORMALS = False, +# EXP_MESH_HQ_NORMALS = False, EXP_ARMATURE = True, EXP_LAMP = True, EXP_CAMERA = True, EXP_EMPTY = True, EXP_IMAGE_COPY = False, - GLOBAL_MATRIX = Matrix(), + GLOBAL_MATRIX = Mathutils.Matrix(), ANIM_ENABLE = True, ANIM_OPTIMIZE = True, ANIM_OPTIMIZE_PRECISSION = 6, ANIM_ACTION_ALL = False, BATCH_ENABLE = False, BATCH_GROUP = True, - BATCH_SCENE = False, BATCH_FILE_PREFIX = '', BATCH_OWN_DIR = False ): @@ -277,23 +338,31 @@ def write(filename, batch_objects = None, \ fbxpath = filename # get the path component of filename - tmp_exists = Blender.sys.exists(fbxpath) + tmp_exists = bpy.sys.exists(fbxpath) +# tmp_exists = Blender.sys.exists(fbxpath) if tmp_exists != 2: # a file, we want a path - while fbxpath and fbxpath[-1] not in ('/', '\\'): - fbxpath = fbxpath[:-1] - if not filename: - Draw.PupMenu('Error%t|Directory does not exist!') + fbxpath = os.path.dirname(fbxpath) +# while fbxpath and fbxpath[-1] not in ('/', '\\'): +# fbxpath = fbxpath[:-1] + if not fbxpath: +# if not filename: + # XXX + print('Error%t|Directory does not exist!') +# Draw.PupMenu('Error%t|Directory does not exist!') return - - tmp_exists = Blender.sys.exists(fbxpath) + + tmp_exists = bpy.sys.exists(fbxpath) +# tmp_exists = Blender.sys.exists(fbxpath) if tmp_exists != 2: - Draw.PupMenu('Error%t|Directory does not exist!') + # XXX + print('Error%t|Directory does not exist!') +# Draw.PupMenu('Error%t|Directory does not exist!') return - if not fbxpath.endswith(Blender.sys.sep): - fbxpath += Blender.sys.sep + if not fbxpath.endswith(os.sep): + fbxpath += os.sep del tmp_exists @@ -303,27 +372,31 @@ def write(filename, batch_objects = None, \ data_seq = bpy.data.scenes # call this function within a loop with BATCH_ENABLE == False - orig_sce = bpy.data.scenes.active + orig_sce = context.scene +# orig_sce = bpy.data.scenes.active new_fbxpath = fbxpath # own dir option modifies, we need to keep an original for data in data_seq: # scene or group - newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name) + newname = BATCH_FILE_PREFIX + cleanName(data.name) +# newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name) if BATCH_OWN_DIR: - new_fbxpath = fbxpath + newname + Blender.sys.sep + new_fbxpath = fbxpath + newname + os.sep # path may alredy exist # TODO - might exist but be a file. unlikely but should probably account for it. - - if Blender.sys.exists(new_fbxpath) == 0: + + if bpy.sys.exists(new_fbxpath) == 0: +# if Blender.sys.exists(new_fbxpath) == 0: os.mkdir(new_fbxpath) filename = new_fbxpath + newname + '.fbx' - print '\nBatch exporting %s as...\n\t"%s"' % (data, filename) - + print('\nBatch exporting %s as...\n\t"%s"' % (data, filename)) + + # XXX don't know what to do with this, probably do the same? (Arystan) if BATCH_GROUP: #group # group, so objects update properly, add a dummy scene. sce = bpy.data.scenes.new() @@ -345,10 +418,11 @@ def write(filename, batch_objects = None, \ # Call self with modified args # Dont pass batch options since we alredy usedt them write(filename, data.objects, + context, False, EXP_MESH, EXP_MESH_APPLY_MOD, - EXP_MESH_HQ_NORMALS, +# EXP_MESH_HQ_NORMALS, EXP_ARMATURE, EXP_LAMP, EXP_CAMERA, @@ -363,7 +437,8 @@ def write(filename, batch_objects = None, \ if BATCH_GROUP: # remove temp group scene - bpy.data.scenes.unlink(sce) + bpy.data.remove_scene(sce) +# bpy.data.scenes.unlink(sce) bpy.data.scenes.active = orig_sce @@ -372,7 +447,9 @@ def write(filename, batch_objects = None, \ # end batch support # Use this for working out paths relative to the export location - basepath = Blender.sys.dirname(filename) + basepath = os.path.dirname(filename) or '.' + basepath += os.sep +# basepath = Blender.sys.dirname(filename) # ---------------------------------------------- # storage classes @@ -398,7 +475,8 @@ def write(filename, batch_objects = None, \ self.blenBone = blenBone self.blenMeshes = {} # fbxMeshObName : mesh self.fbxArm = fbxArm - self.restMatrix = blenBone.matrix['ARMATURESPACE'] + self.restMatrix = blenBone.armature_matrix +# self.restMatrix = blenBone.matrix['ARMATURESPACE'] # not used yet # self.restMatrixInv = self.restMatrix.copy().invert() @@ -407,8 +485,10 @@ def write(filename, batch_objects = None, \ self.parent = None # not public - pose = fbxArm.blenObject.getPose() - self.__pose_bone = pose.bones[self.blenName] + pose = fbxArm.blenObject.pose +# pose = fbxArm.blenObject.getPose() + self.__pose_bone = pose.pose_channels[self.blenName] +# self.__pose_bone = pose.bones[self.blenName] # store a list if matricies here, (poseMatrix, head, tail) # {frame:posematrix, frame:posematrix, ...} @@ -431,8 +511,9 @@ def write(filename, batch_objects = None, \ self.__pose_bone.head.copy(),\ self.__pose_bone.tail.copy() ) ''' - - self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy() + + self.__anim_poselist[f] = self.__pose_bone.pose_matrix.copy() +# self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy() # get pose from frame. def getPoseMatrix(self, f):# ---------------------------------------------- @@ -473,7 +554,8 @@ def write(filename, batch_objects = None, \ self.fbxGroupNames = [] self.fbxParent = None # set later on IF the parent is in the selection. if matrixWorld: self.matrixWorld = matrixWorld * GLOBAL_MATRIX - else: self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX + else: self.matrixWorld = ob.matrix * GLOBAL_MATRIX +# else: self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX self.__anim_poselist = {} # we should only access this def parRelMatrix(self): @@ -483,7 +565,8 @@ def write(filename, batch_objects = None, \ return self.matrixWorld def setPoseFrame(self, f): - self.__anim_poselist[f] = self.blenObject.matrixWorld.copy() + self.__anim_poselist[f] = self.blenObject.matrix.copy() +# self.__anim_poselist[f] = self.blenObject.matrixWorld.copy() def getAnimParRelMatrix(self, frame): if self.fbxParent: @@ -500,11 +583,12 @@ def write(filename, batch_objects = None, \ matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotationPart() # Lamps need to be rotated - if type =='Lamp': + if type =='LAMP': matrix_rot = mtx_x90 * matrix_rot - elif ob and type =='Camera': - y = Vector(0,1,0) * matrix_rot - matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + elif type =='CAMERA': +# elif ob and type =='Camera': + y = Mathutils.Vector(0,1,0) * matrix_rot + matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, 'r', y) return matrix_rot @@ -514,14 +598,16 @@ def write(filename, batch_objects = None, \ - print '\nFBX export starting...', filename - start_time = Blender.sys.time() + print('\nFBX export starting...', filename) + start_time = time.clock() +# start_time = Blender.sys.time() try: file = open(filename, 'w') except: return False - - sce = bpy.data.scenes.active + + sce = context.scene +# sce = bpy.data.scenes.active world = sce.world @@ -553,7 +639,8 @@ def write(filename, batch_objects = None, \ }''' % (curtime)) file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime) - file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version')) + file.write('\nCreator: "Blender3D version 2.5"') +# file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version')) pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect allong the way @@ -562,16 +649,19 @@ def write(filename, batch_objects = None, \ ''' Matrix mod is so armature objects can modify their bone matricies ''' - if isinstance(ob, Blender.Types.BoneType): + if isinstance(ob, bpy.types.Bone): +# if isinstance(ob, Blender.Types.BoneType): # we know we have a matrix # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod) - matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + matrix = mtx4_z90 * ob.armature_matrix # dont apply armature matrix anymore +# matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore parent = ob.parent if parent: #par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod) - par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + par_matrix = mtx4_z90 * parent.armature_matrix # dont apply armature matrix anymore +# par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore matrix = matrix * par_matrix.copy().invert() matrix_rot = matrix.rotationPart() @@ -583,7 +673,7 @@ def write(filename, batch_objects = None, \ else: # This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore #if ob and not matrix: matrix = ob.matrixWorld * GLOBAL_MATRIX - if ob and not matrix: raise "error: this should never happen!" + if ob and not matrix: raise Exception("error: this should never happen!") matrix_rot = matrix #if matrix: @@ -599,8 +689,8 @@ def write(filename, batch_objects = None, \ matrix_rot = mtx_x90 * matrix_rot rot = tuple(matrix_rot.toEuler()) elif ob and ob.type =='Camera': - y = Vector(0,1,0) * matrix_rot - matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + y = Mathutils.Vector(0,1,0) * matrix_rot + matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, 'r', y) rot = tuple(matrix_rot.toEuler()) else: rot = tuple(matrix_rot.toEuler()) @@ -621,7 +711,8 @@ def write(filename, batch_objects = None, \ loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod) file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc) - file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot) + file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % tuple(eulerRadToDeg(rot))) +# file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot) file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale) return loc, rot, scale, matrix, matrix_rot @@ -708,7 +799,8 @@ def write(filename, batch_objects = None, \ Property: "Show", "bool", "",1 Property: "NegativePercentShapeSupport", "bool", "",1 Property: "DefaultAttributeIndex", "int", "",0''') - if ob and type(ob) != Blender.Types.BoneType: + if ob and not isinstance(ob, bpy.types.Bone): +# if ob and type(ob) != Blender.Types.BoneType: # Only mesh objects have color file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8') file.write('\n\t\t\tProperty: "Size", "double", "",100') @@ -738,8 +830,9 @@ def write(filename, batch_objects = None, \ ((my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length) """ - file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\ - (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length) + file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' % + (my_bone.blenBone.armature_head - my_bone.blenBone.armature_tail).length) +# (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length) #file.write('\n\t\t\tProperty: "LimbLength", "double", "",1') file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8') @@ -878,9 +971,12 @@ def write(filename, batch_objects = None, \ ''' Write a blender camera ''' - render = sce.render - width = render.sizeX - height = render.sizeY + render = sce.render_data + width = render.resolution_x + height = render.resolution_y +# render = sce.render +# width = render.sizeX +# height = render.sizeY aspect = float(width)/height data = my_cam.blenObject.data @@ -894,8 +990,10 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1') file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1') file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",14.0323972702026') - file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units? - file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto + file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shift_x) # not sure if this is in the correct units? +# file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units? + file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shift_y) # ditto +# file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0,0,0') file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0') file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1') @@ -927,8 +1025,10 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0') file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1') file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0') - file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart) - file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart) + file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clip_start) +# file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart) + file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clip_end) +# file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart) file.write('\n\t\t\tProperty: "FilmWidth", "double", "",1.0') file.write('\n\t\t\tProperty: "FilmHeight", "double", "",1.0') file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",%.6f' % aspect) @@ -975,8 +1075,8 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tTypeFlags: "Camera"') file.write('\n\t\tGeometryVersion: 124') file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc) - file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector(0,1,0) * matrix_rot) ) - file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector(0,0,-1)*matrix_rot) ) + file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Mathutils.Vector(0,1,0) * matrix_rot) ) + file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Mathutils.Vector(0,0,-1)*matrix_rot) ) #file.write('\n\t\tUp: 0,0,0' ) #file.write('\n\t\tLookAt: 0,0,0' ) @@ -1001,16 +1101,20 @@ def write(filename, batch_objects = None, \ #ePOINT, #eDIRECTIONAL #eSPOT - light_type = light.type + light_type_items = {'POINT': 0, 'SUN': 1, 'SPOT': 2, 'HEMI': 3, 'AREA': 4} + light_type = light_type_items[light.type] +# light_type = light.type if light_type > 2: light_type = 1 # hemi and area lights become directional - - mode = light.mode - if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows: + +# mode = light.mode + if light.shadow_method == 'RAY_SHADOW' or light.shadow_method == 'BUFFER_SHADOW': +# if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows: do_shadow = 1 else: do_shadow = 0 - - if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular): + + if light.only_shadow or (not light.diffuse and not light.specular): +# if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular): do_light = 0 else: do_light = 1 @@ -1025,11 +1129,16 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "GoboProperty", "object", ""') file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1') file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 - file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) + if light.type == 'SPOT': + file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spot_size * scale)) +# file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') - file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col)) + file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.color)) +# file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col)) file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 - file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) +# + # duplication? see ^ (Arystan) +# file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type) file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",%i' % do_light) @@ -1038,7 +1147,8 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1') file.write('\n\t\t\tProperty: "GoboProperty", "object", ""') file.write('\n\t\t\tProperty: "DecayType", "enum", "",0') - file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist) + file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.distance) +# file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist) file.write('\n\t\t\tProperty: "EnableNearAttenuation", "bool", "",0') file.write('\n\t\t\tProperty: "NearAttenuationStart", "double", "",0') file.write('\n\t\t\tProperty: "NearAttenuationEnd", "double", "",0') @@ -1084,7 +1194,8 @@ def write(filename, batch_objects = None, \ }''') # Material Settings - if world: world_amb = world.getAmb() + if world: world_amb = tuple(world.ambient_color) +# if world: world_amb = world.getAmb() else: world_amb = (0,0,0) # Default value def write_material(matname, mat): @@ -1092,22 +1203,31 @@ def write(filename, batch_objects = None, \ # Todo, add more material Properties. if mat: - mat_cold = tuple(mat.rgbCol) - mat_cols = tuple(mat.specCol) + mat_cold = tuple(mat.diffuse_color) +# mat_cold = tuple(mat.rgbCol) + mat_cols = tuple(mat.specular_color) +# mat_cols = tuple(mat.specCol) #mat_colm = tuple(mat.mirCol) # we wont use the mirror color - mat_colamb = tuple([c for c in world_amb]) - - mat_dif = mat.ref - mat_amb = mat.amb - mat_hard = (float(mat.hard)-1)/5.10 - mat_spec = mat.spec/2.0 + mat_colamb = world_amb +# mat_colamb = tuple([c for c in world_amb]) + + mat_dif = mat.diffuse_intensity +# mat_dif = mat.ref + mat_amb = mat.ambient +# mat_amb = mat.amb + mat_hard = (float(mat.specular_hardness)-1)/5.10 +# mat_hard = (float(mat.hard)-1)/5.10 + mat_spec = mat.specular_intensity/2.0 +# mat_spec = mat.spec/2.0 mat_alpha = mat.alpha mat_emit = mat.emit - mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS + mat_shadeless = mat.shadeless +# mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS if mat_shadeless: mat_shader = 'Lambert' else: - if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT: + if mat.diffuse_shader == 'LAMBERT': +# if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT: mat_shader = 'Lambert' else: mat_shader = 'Phong' @@ -1159,7 +1279,20 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t}') file.write('\n\t}') - + + def copy_image(image): + + rel = image.get_export_path(basepath, True) + base = os.path.basename(rel) + + if EXP_IMAGE_COPY: + absp = image.get_export_path(basepath, False) + if not os.path.exists(absp): + shutil.copy(image.get_abs_filename(), absp) + + return (rel, base) + + # tex is an Image (Arystan) def write_video(texname, tex): # Same as texture really! file.write('\n\tVideo: "Video::%s", "Clip" {' % texname) @@ -1172,7 +1305,8 @@ def write(filename, batch_objects = None, \ Property: "Width", "int", "",0 Property: "Height", "int", "",0''') if tex: - fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) + fname_rel, fname_strip = copy_image(tex) +# fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -1221,9 +1355,11 @@ def write(filename, batch_objects = None, \ Property: "UseMipMap", "bool", "",0 Property: "CurrentMappingType", "enum", "",0 Property: "UVSwap", "bool", "",0''') - - file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX) - file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY) + + file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clamp_x) +# file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX) + file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clamp_y) +# file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY) file.write(''' Property: "TextureRotationPivot", "Vector3D", "",0,0,0 @@ -1234,7 +1370,8 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tMedia: "Video::%s"' % texname) if tex: - fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) + fname_rel, fname_strip = copy_image(tex) +# fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -1290,7 +1427,7 @@ def write(filename, batch_objects = None, \ # TODO - this is a bit lazy, we could have a simple write loop # for this case because all weights are 1.0 but for now this is ok # Parent Bones arent used all that much anyway. - vgroup_data = [(j, 1.0) for j in xrange(len(my_mesh.blenData.verts))] + vgroup_data = [(j, 1.0) for j in range(len(my_mesh.blenData.verts))] else: # This bone is not a parent of this mesh object, no weights vgroup_data = [] @@ -1358,7 +1495,8 @@ def write(filename, batch_objects = None, \ if my_mesh.blenTextures: do_textures = True else: do_textures = False - do_uvs = me.faceUV + do_uvs = len(me.uv_textures) > 0 +# do_uvs = me.faceUV file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName) @@ -1390,20 +1528,25 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tPolygonVertexIndex: ') i=-1 for f in me.faces: - fi = [v.index for v in f] + fi = f.verts + # fi = [v_index for j, v_index in enumerate(f.verts) if v_index != 0 or j != 3] +# fi = [v.index for v in f] + # flip the last index, odd but it looks like # this is how fbx tells one face from another fi[-1] = -(fi[-1]+1) fi = tuple(fi) if i==-1: - if len(f) == 3: file.write('%i,%i,%i' % fi ) + if len(fi) == 3: file.write('%i,%i,%i' % fi ) +# if len(f) == 3: file.write('%i,%i,%i' % fi ) else: file.write('%i,%i,%i,%i' % fi ) i=0 else: if i==13: file.write('\n\t\t') i=0 - if len(f) == 3: file.write(',%i,%i,%i' % fi ) + if len(fi) == 3: file.write(',%i,%i,%i' % fi ) +# if len(f) == 3: file.write(',%i,%i,%i' % fi ) else: file.write(',%i,%i,%i,%i' % fi ) i+=1 @@ -1411,13 +1554,15 @@ def write(filename, batch_objects = None, \ i=-1 for ed in me.edges: if i==-1: - file.write('%i,%i' % (ed.v1.index, ed.v2.index)) + file.write('%i,%i' % (ed.verts[0], ed.verts[1])) +# file.write('%i,%i' % (ed.v1.index, ed.v2.index)) i=0 else: if i==13: file.write('\n\t\t') i=0 - file.write(',%i,%i' % (ed.v1.index, ed.v2.index)) + file.write(',%i,%i' % (ed.verts[0], ed.verts[1])) +# file.write(',%i,%i' % (ed.v1.index, ed.v2.index)) i+=1 file.write('\n\t\tGeometryVersion: 124') @@ -1433,11 +1578,13 @@ def write(filename, batch_objects = None, \ i=-1 for v in me.verts: if i==-1: - file.write('%.15f,%.15f,%.15f' % tuple(v.no)); i=0 + file.write('%.15f,%.15f,%.15f' % tuple(v.normal)); i=0 +# file.write('%.15f,%.15f,%.15f' % tuple(v.no)); i=0 else: if i==2: file.write('\n '); i=0 - file.write(',%.15f,%.15f,%.15f' % tuple(v.no)) + file.write(',%.15f,%.15f,%.15f' % tuple(v.normal)) +# file.write(',%.15f,%.15f,%.15f' % tuple(v.no)) i+=1 file.write('\n\t\t}') @@ -1464,39 +1611,53 @@ def write(filename, batch_objects = None, \ # Write Edge Smoothing file.write(''' - LayerElementSmoothing: 1 { + LayerElementSmoothing: 0 { Version: 101 Name: "" MappingInformationType: "ByEdge" ReferenceInformationType: "Direct" Smoothing: ''') - SHARP = Blender.Mesh.EdgeFlags.SHARP +# SHARP = Blender.Mesh.EdgeFlags.SHARP i=-1 for ed in me.edges: if i==-1: - file.write('%i' % ((ed.flag&SHARP)!=0)); i=0 + file.write('%i' % (ed.sharp)); i=0 +# file.write('%i' % ((ed.flag&SHARP)!=0)); i=0 else: if i==54: file.write('\n '); i=0 - file.write(',%i' % ((ed.flag&SHARP)!=0)) + file.write(',%i' % (ed.sharp)) +# file.write(',%i' % ((ed.flag&SHARP)!=0)) i+=1 file.write('\n\t\t}') - del SHARP - +# del SHARP + + # small utility function + # returns a slice of data depending on number of face verts + # data is either a MeshTextureFace or MeshColor + def face_data(data, face): + totvert = len(f.verts) + + return data[:totvert] + # Write VertexColor Layers # note, no programs seem to use this info :/ collayers = [] - if me.vertexColors: - collayers = me.getColorLayerNames() - collayer_orig = me.activeColorLayer + if len(me.vertex_colors): +# if me.vertexColors: + collayers = me.vertex_colors +# collayers = me.getColorLayerNames() + collayer_orig = me.active_vertex_color +# collayer_orig = me.activeColorLayer for colindex, collayer in enumerate(collayers): - me.activeColorLayer = collayer +# me.activeColorLayer = collayer file.write('\n\t\tLayerElementColor: %i {' % colindex) file.write('\n\t\t\tVersion: 101') - file.write('\n\t\t\tName: "%s"' % collayer) + file.write('\n\t\t\tName: "%s"' % collayer.name) +# file.write('\n\t\t\tName: "%s"' % collayer) file.write(''' MappingInformationType: "ByPolygonVertex" @@ -1505,23 +1666,41 @@ def write(filename, batch_objects = None, \ i = -1 ii = 0 # Count how many Colors we write - - for f in me.faces: - for col in f.col: + + for f, cf in zip(me.faces, collayer.data): + colors = [cf.color1, cf.color2, cf.color3, cf.color4] + + # determine number of verts + colors = face_data(colors, f) + + for col in colors: if i==-1: - file.write('%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) + file.write('%.4f,%.4f,%.4f,1' % tuple(col)) i=0 else: if i==7: file.write('\n\t\t\t\t') i=0 - file.write(',%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) + file.write(',%.4f,%.4f,%.4f,1' % tuple(col)) i+=1 ii+=1 # One more Color + +# for f in me.faces: +# for col in f.col: +# if i==-1: +# file.write('%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) +# i=0 +# else: +# if i==7: +# file.write('\n\t\t\t\t') +# i=0 +# file.write(',%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0)) +# i+=1 +# ii+=1 # One more Color file.write('\n\t\t\tColorIndex: ') i = -1 - for j in xrange(ii): + for j in range(ii): if i == -1: file.write('%i' % j) i=0 @@ -1539,13 +1718,17 @@ def write(filename, batch_objects = None, \ # Write UV and texture layers. uvlayers = [] if do_uvs: - uvlayers = me.getUVLayerNames() - uvlayer_orig = me.activeUVLayer - for uvindex, uvlayer in enumerate(uvlayers): - me.activeUVLayer = uvlayer + uvlayers = me.uv_textures +# uvlayers = me.getUVLayerNames() + uvlayer_orig = me.active_uv_texture +# uvlayer_orig = me.activeUVLayer + for uvindex, uvlayer in enumerate(me.uv_textures): +# for uvindex, uvlayer in enumerate(uvlayers): +# me.activeUVLayer = uvlayer file.write('\n\t\tLayerElementUV: %i {' % uvindex) file.write('\n\t\t\tVersion: 101') - file.write('\n\t\t\tName: "%s"' % uvlayer) + file.write('\n\t\t\tName: "%s"' % uvlayer.name) +# file.write('\n\t\t\tName: "%s"' % uvlayer) file.write(''' MappingInformationType: "ByPolygonVertex" @@ -1555,8 +1738,10 @@ def write(filename, batch_objects = None, \ i = -1 ii = 0 # Count how many UVs we write - for f in me.faces: - for uv in f.uv: + for uf in uvlayer.data: +# for f in me.faces: + for uv in uf.uv: +# for uv in f.uv: if i==-1: file.write('%.6f,%.6f' % tuple(uv)) i=0 @@ -1570,7 +1755,7 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t\tUVIndex: ') i = -1 - for j in xrange(ii): + for j in range(ii): if i == -1: file.write('%i' % j) i=0 @@ -1586,7 +1771,8 @@ def write(filename, batch_objects = None, \ if do_textures: file.write('\n\t\tLayerElementTexture: %i {' % uvindex) file.write('\n\t\t\tVersion: 101') - file.write('\n\t\t\tName: "%s"' % uvlayer) + file.write('\n\t\t\tName: "%s"' % uvlayer.name) +# file.write('\n\t\t\tName: "%s"' % uvlayer) if len(my_mesh.blenTextures) == 1: file.write('\n\t\t\tMappingInformationType: "AllSame"') @@ -1610,7 +1796,8 @@ def write(filename, batch_objects = None, \ i+=1 i=-1 - for f in me.faces: + for f in uvlayer.data: +# for f in me.faces: img_key = f.image if i==-1: @@ -1636,7 +1823,7 @@ def write(filename, batch_objects = None, \ TextureId: ''') file.write('\n\t\t}') - me.activeUVLayer = uvlayer_orig +# me.activeUVLayer = uvlayer_orig # Done with UV/textures. @@ -1665,13 +1852,21 @@ def write(filename, batch_objects = None, \ len_material_mapping_local = len(material_mapping_local) mats = my_mesh.blenMaterialList + + if me.active_uv_texture: + uv_faces = me.active_uv_texture.data + else: + uv_faces = [None] * len(me.faces) i=-1 - for f in me.faces: - try: mat = mats[f.mat] + for f, uf in zip(me.faces, uv_faces): +# for f in me.faces: + try: mat = mats[f.material_index] +# try: mat = mats[f.mat] except:mat = None - if do_uvs: tex = f.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/ + if do_uvs: tex = uf.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/ +# if do_uvs: tex = f.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/ else: tex = None if i==-1: @@ -1710,7 +1905,8 @@ def write(filename, batch_objects = None, \ TypedIndex: 0 }''') - if me.vertexColors: + if me.vertex_colors: +# if me.vertexColors: file.write(''' LayerElement: { Type: "LayerElementColor" @@ -1728,7 +1924,7 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t}') if len(uvlayers) > 1: - for i in xrange(1, len(uvlayers)): + for i in range(1, len(uvlayers)): file.write('\n\t\tLayer: %i {' % i) file.write('\n\t\t\tVersion: 100') @@ -1756,7 +1952,7 @@ def write(filename, batch_objects = None, \ layer_offset = 0 if uvlayers: layer_offset = len(uvlayers)-1 - for i in xrange(layer_offset, len(collayers)+layer_offset): + for i in range(layer_offset, len(collayers)+layer_offset): file.write('\n\t\tLayer: %i {' % i) file.write('\n\t\t\tVersion: 100') @@ -1806,7 +2002,8 @@ def write(filename, batch_objects = None, \ # if EXP_OBS_SELECTED is false, use sceens objects if not batch_objects: - if EXP_OBS_SELECTED: tmp_objects = sce.objects.context + if EXP_OBS_SELECTED: tmp_objects = context.selected_objects +# if EXP_OBS_SELECTED: tmp_objects = sce.objects.context else: tmp_objects = sce.objects else: tmp_objects = batch_objects @@ -1815,43 +2012,63 @@ def write(filename, batch_objects = None, \ # This is needed so applying modifiers dosnt apply the armature deformation, its also needed # ...so mesh objects return their rest worldspace matrix when bone-parents are exported as weighted meshes. # set every armature to its rest, backup the original values so we done mess up the scene - ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures] + ob_arms_orig_rest = [arm.rest_position for arm in bpy.data.armatures] +# ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures] for arm in bpy.data.armatures: - arm.restPosition = True + arm.rest_position = True +# arm.restPosition = True if ob_arms_orig_rest: for ob_base in bpy.data.objects: #if ob_base.type == 'Armature': - ob_base.makeDisplayList() + ob_base.make_display_list() +# ob_base.makeDisplayList() # This causes the makeDisplayList command to effect the mesh - Blender.Set('curframe', Blender.Get('curframe')) + sce.set_frame(sce.current_frame) +# Blender.Set('curframe', Blender.Get('curframe')) for ob_base in tmp_objects: - for ob, mtx in BPyObject.getDerivedObjects(ob_base): - #for ob in [ob_base,]: + + # ignore dupli children + if ob_base.parent and ob_base.parent.dupli_type != 'NONE': + continue + + obs = [(ob_base, ob_base.matrix)] + if ob_base.dupli_type != 'NONE': + ob_base.create_dupli_list() + obs = [(dob.object, dob.matrix) for dob in ob_base.dupli_list] + + for ob, mtx in obs: +# for ob, mtx in BPyObject.getDerivedObjects(ob_base): tmp_ob_type = ob.type - if tmp_ob_type == 'Camera': + if tmp_ob_type == 'CAMERA': +# if tmp_ob_type == 'Camera': if EXP_CAMERA: ob_cameras.append(my_object_generic(ob, mtx)) - elif tmp_ob_type == 'Lamp': + elif tmp_ob_type == 'LAMP': +# elif tmp_ob_type == 'Lamp': if EXP_LAMP: ob_lights.append(my_object_generic(ob, mtx)) - elif tmp_ob_type == 'Armature': + elif tmp_ob_type == 'ARMATURE': +# elif tmp_ob_type == 'Armature': if EXP_ARMATURE: # TODO - armatures dont work in dupligroups! if ob not in ob_arms: ob_arms.append(ob) # ob_arms.append(ob) # replace later. was "ob_arms.append(sane_obname(ob), ob)" - elif tmp_ob_type == 'Empty': + elif tmp_ob_type == 'EMPTY': +# elif tmp_ob_type == 'Empty': if EXP_EMPTY: ob_null.append(my_object_generic(ob, mtx)) elif EXP_MESH: origData = True - if tmp_ob_type != 'Mesh': - me = bpy.data.meshes.new() - try: me.getFromObject(ob) + if tmp_ob_type != 'MESH': +# if tmp_ob_type != 'Mesh': +# me = bpy.data.meshes.new() + try: me = ob.create_mesh(True, 'PREVIEW') +# try: me.getFromObject(ob) except: me = None if me: meshes_to_clear.append( me ) @@ -1860,63 +2077,71 @@ def write(filename, batch_objects = None, \ else: # Mesh Type! if EXP_MESH_APPLY_MOD: - me = bpy.data.meshes.new() - me.getFromObject(ob) +# me = bpy.data.meshes.new() + me = ob.create_mesh(True, 'PREVIEW') +# me.getFromObject(ob) # so we keep the vert groups - if EXP_ARMATURE: - orig_mesh = ob.getData(mesh=1) - if orig_mesh.getVertGroupNames(): - ob.copy().link(me) - # If new mesh has no vgroups we can try add if verts are teh same - if not me.getVertGroupNames(): # vgroups were not kept by the modifier - if len(me.verts) == len(orig_mesh.verts): - groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh) - BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) +# if EXP_ARMATURE: +# orig_mesh = ob.getData(mesh=1) +# if orig_mesh.getVertGroupNames(): +# ob.copy().link(me) +# # If new mesh has no vgroups we can try add if verts are teh same +# if not me.getVertGroupNames(): # vgroups were not kept by the modifier +# if len(me.verts) == len(orig_mesh.verts): +# groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh) +# BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) # print ob, me, me.getVertGroupNames() meshes_to_clear.append( me ) origData = False mats = me.materials else: - me = ob.getData(mesh=1) + me = ob.data +# me = ob.getData(mesh=1) mats = me.materials - # Support object colors - tmp_colbits = ob.colbits - if tmp_colbits: - tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too. - for i in xrange(16): - if tmp_colbits & (1<<i): - mats[i] = tmp_ob_mats[i] - del tmp_ob_mats - del tmp_colbits +# # Support object colors +# tmp_colbits = ob.colbits +# if tmp_colbits: +# tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too. +# for i in xrange(16): +# if tmp_colbits & (1<<i): +# mats[i] = tmp_ob_mats[i] +# del tmp_ob_mats +# del tmp_colbits if me: - # This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled. - # so strictly this is bad. but only in rare cases would it have negative results - # say with dupliverts the objects would rotate a bit differently - if EXP_MESH_HQ_NORMALS: - BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines. +# # This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled. +# # so strictly this is bad. but only in rare cases would it have negative results +# # say with dupliverts the objects would rotate a bit differently +# if EXP_MESH_HQ_NORMALS: +# BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines. texture_mapping_local = {} material_mapping_local = {} - if me.faceUV: - uvlayer_orig = me.activeUVLayer - for uvlayer in me.getUVLayerNames(): - me.activeUVLayer = uvlayer - for f in me.faces: - tex = f.image + if len(me.uv_textures) > 0: +# if me.faceUV: + uvlayer_orig = me.active_uv_texture +# uvlayer_orig = me.activeUVLayer + for uvlayer in me.uv_textures: +# for uvlayer in me.getUVLayerNames(): +# me.activeUVLayer = uvlayer + for f, uf in zip(me.faces, uvlayer.data): +# for f in me.faces: + tex = uf.image +# tex = f.image textures[tex] = texture_mapping_local[tex] = None - try: mat = mats[f.mat] + try: mat = mats[f.material_index] +# try: mat = mats[f.mat] except: mat = None materials[mat, tex] = material_mapping_local[mat, tex] = None # should use sets, wait for blender 2.5 - me.activeUVLayer = uvlayer_orig +# me.activeUVLayer = uvlayer_orig else: for mat in mats: # 2.44 use mat.lib too for uniqueness @@ -1925,13 +2150,16 @@ def write(filename, batch_objects = None, \ materials[None, None] = None if EXP_ARMATURE: - armob = BPyObject.getObjectArmature(ob) + armob = ob.find_armature() blenParentBoneName = None # parent bone - special case - if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE: + if (not armob) and ob.parent and ob.parent.type == 'ARMATURE' and \ + ob.parent_type == 'BONE': +# if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE: armob = ob.parent - blenParentBoneName = ob.parentbonename + blenParentBoneName = ob.parent_bone +# blenParentBoneName = ob.parentbonename if armob and armob not in ob_arms: @@ -1943,9 +2171,9 @@ def write(filename, batch_objects = None, \ my_mesh = my_object_generic(ob, mtx) my_mesh.blenData = me my_mesh.origData = origData - my_mesh.blenMaterials = material_mapping_local.keys() + my_mesh.blenMaterials = list(material_mapping_local.keys()) my_mesh.blenMaterialList = mats - my_mesh.blenTextures = texture_mapping_local.keys() + my_mesh.blenTextures = list(texture_mapping_local.keys()) # if only 1 null texture then empty the list if len(my_mesh.blenTextures) == 1 and my_mesh.blenTextures[0] == None: @@ -1955,18 +2183,26 @@ def write(filename, batch_objects = None, \ my_mesh.fbxBoneParent = blenParentBoneName # replace with my_bone instance later ob_meshes.append( my_mesh ) - + + # not forgetting to free dupli_list + if ob_base.dupli_list: ob_base.free_dupli_list() + + if EXP_ARMATURE: # now we have the meshes, restore the rest arm position for i, arm in enumerate(bpy.data.armatures): - arm.restPosition = ob_arms_orig_rest[i] + arm.rest_position = ob_arms_orig_rest[i] +# arm.restPosition = ob_arms_orig_rest[i] if ob_arms_orig_rest: for ob_base in bpy.data.objects: - if ob_base.type == 'Armature': - ob_base.makeDisplayList() + if ob_base.type == 'ARMATURE': +# if ob_base.type == 'Armature': + ob_base.make_display_list() +# ob_base.makeDisplayList() # This causes the makeDisplayList command to effect the mesh - Blender.Set('curframe', Blender.Get('curframe')) + sce.set_frame(sce.current_frame) +# Blender.Set('curframe', Blender.Get('curframe')) del tmp_ob_type, tmp_objects @@ -1977,13 +2213,18 @@ def write(filename, batch_objects = None, \ my_arm.fbxBones = [] my_arm.blenData = ob.data - my_arm.blenAction = ob.action + if ob.animation_data: + my_arm.blenAction = ob.animation_data.action + else: + my_arm.blenAction = None +# my_arm.blenAction = ob.action my_arm.blenActionList = [] # fbxName, blenderObject, my_bones, blenderActions #ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, []) - for bone in my_arm.blenData.bones.values(): + for bone in my_arm.blenData.bones: +# for bone in my_arm.blenData.bones.values(): my_bone = my_bone_class(bone, my_arm) my_arm.fbxBones.append( my_bone ) ob_bones.append( my_bone ) @@ -2032,18 +2273,25 @@ def write(filename, batch_objects = None, \ # Build blenObject -> fbxObject mapping # this is needed for groups as well as fbxParenting - bpy.data.objects.tag = False +# for ob in bpy.data.objects: ob.tag = False +# bpy.data.objects.tag = False + + # using a list of object names for tagging (Arystan) + tagged_objects = [] + tmp_obmapping = {} for ob_generic in ob_all_typegroups: for ob_base in ob_generic: - ob_base.blenObject.tag = True + tagged_objects.append(ob_base.blenObject.name) +# ob_base.blenObject.tag = True tmp_obmapping[ob_base.blenObject] = ob_base # Build Groups from objects we export for blenGroup in bpy.data.groups: fbxGroupName = None for ob in blenGroup.objects: - if ob.tag: + if ob.name in tagged_objects: +# if ob.tag: if fbxGroupName == None: fbxGroupName = sane_groupname(blenGroup) groups.append((fbxGroupName, blenGroup)) @@ -2056,7 +2304,8 @@ def write(filename, batch_objects = None, \ for ob_generic in ob_all_typegroups: for my_ob in ob_generic: parent = my_ob.blenObject.parent - if parent and parent.tag: # does it exist and is it in the mapping + if parent and parent.name in tagged_objects: # does it exist and is it in the mapping +# if parent and parent.tag: # does it exist and is it in the mapping my_ob.fbxParent = tmp_obmapping[parent] @@ -2064,8 +2313,8 @@ def write(filename, batch_objects = None, \ # Finished finding groups we use - materials = [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.iterkeys()] - textures = [(sane_texname(tex), tex) for tex in textures.iterkeys() if tex] + materials = [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.keys()] + textures = [(sane_texname(tex), tex) for tex in textures.keys() if tex] materials.sort() # sort by name textures.sort() @@ -2220,11 +2469,12 @@ Objects: {''') if my_mesh.fbxBoneParent: weights = None else: - weights = meshNormalizedWeights(my_mesh.blenData) + weights = meshNormalizedWeights(my_mesh.blenObject) +# weights = meshNormalizedWeights(my_mesh.blenData) #for bonename, bone, obname, bone_mesh, armob in ob_bones: for my_bone in ob_bones: - if me in my_bone.blenMeshes.itervalues(): + if me in iter(my_bone.blenMeshes.values()): write_sub_deformer_skin(my_mesh, my_bone, weights) # Write pose's really weired, only needed when an armature and mesh are used together @@ -2426,7 +2676,8 @@ Connections: {''') # Needed for scene footer as well as animation - render = sce.render + render = sce.render_data +# render = sce.render # from the FBX sdk #define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000)) @@ -2435,8 +2686,10 @@ Connections: {''') return int(0.5 + ((t/fps) * 46186158000)) fps = float(render.fps) - start = render.sFrame - end = render.eFrame + start = sce.start_frame +# start = render.sFrame + end = sce.end_frame +# end = render.eFrame if end < start: start, end = end, start if start==end: ANIM_ENABLE = False @@ -2445,7 +2698,8 @@ Connections: {''') if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]: - frame_orig = Blender.Get('curframe') + frame_orig = sce.current_frame +# frame_orig = Blender.Get('curframe') if ANIM_OPTIMIZE: ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** ANIM_OPTIMIZE_PRECISSION @@ -2454,9 +2708,12 @@ Connections: {''') tmp_actions = [None] # None is the default action blenActionDefault = None action_lastcompat = None + + # instead of tagging + tagged_actions = [] if ANIM_ACTION_ALL: - bpy.data.actions.tag = False +# bpy.data.actions.tag = False tmp_actions = list(bpy.data.actions) @@ -2472,12 +2729,14 @@ Connections: {''') arm_bone_names = set([my_bone.blenName for my_bone in my_arm.fbxBones]) for action in tmp_actions: - - action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) ) + + action_chan_names = arm_bone_names.intersection( set([g.name for g in action.groups]) ) +# action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) ) if action_chan_names: # at least one channel matches. my_arm.blenActionList.append(action) - action.tag = True + tagged_actions.append(action.name) +# action.tag = True tmp_act_count += 1 # incase there is no actions applied to armatures @@ -2504,10 +2763,11 @@ Takes: {''') for blenAction in tmp_actions: # we have tagged all actious that are used be selected armatures if blenAction: - if blenAction.tag: - print '\taction: "%s" exporting...' % blenAction.name + if blenAction.name in tagged_actions: +# if blenAction.tag: + print('\taction: "%s" exporting...' % blenAction.name) else: - print '\taction: "%s" has no armature using it, skipping' % blenAction.name + print('\taction: "%s" has no armature using it, skipping' % blenAction.name) continue if blenAction == None: @@ -2521,17 +2781,18 @@ Takes: {''') file.write('\n\tTake: "%s" {' % sane_name_mapping_take[blenAction.name]) else: file.write('\n\tTake: "%s" {' % sane_takename(blenAction)) - - tmp = blenAction.getFrameNumbers() - if tmp: - act_start = min(tmp) - act_end = max(tmp) - del tmp - else: - # Fallback on this, theres not much else we can do? :/ - # when an action has no length - act_start = start - act_end = end + + act_start, act_end = blenAction.get_frame_range() +# tmp = blenAction.getFrameNumbers() +# if tmp: +# act_start = min(tmp) +# act_end = max(tmp) +# del tmp +# else: +# # Fallback on this, theres not much else we can do? :/ +# # when an action has no length +# act_start = start +# act_end = end # Set the action active for my_bone in ob_arms: @@ -2558,7 +2819,8 @@ Takes: {''') ''' i = act_start while i <= act_end: - Blender.Set('curframe', i) + sce.set_frame(i) +# Blender.Set('curframe', i) for ob_generic in ob_anim_lists: for my_ob in ob_generic: #Blender.Window.RedrawAll() @@ -2585,7 +2847,7 @@ Takes: {''') file.write('\n\t\t\tVersion: 1.1') file.write('\n\t\t\tChannel: "Transform" {') - context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in xrange(act_start, act_end+1) ] + context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in range(act_start, act_end+1) ] # ---------------- # ---------------- @@ -2603,11 +2865,12 @@ Takes: {''') for mtx in context_bone_anim_mats: if prev_eul: prev_eul = mtx[1].toEuler(prev_eul) else: prev_eul = mtx[1].toEuler() - context_bone_anim_vecs.append(prev_eul) + context_bone_anim_vecs.append(eulerRadToDeg(prev_eul)) +# context_bone_anim_vecs.append(prev_eul) file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation - for i in xrange(3): + for i in range(3): # Loop on each axis of the bone file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] ) @@ -2694,8 +2957,9 @@ Takes: {''') my_bone.blenObject.action = my_bone.blenAction file.write('\n}') - - Blender.Set('curframe', frame_orig) + + sce.set_frame(frame_orig) +# Blender.Set('curframe', frame_orig) else: # no animation @@ -2713,15 +2977,21 @@ Takes: {''') # Clear mesh data Only when writing with modifiers applied for me in meshes_to_clear: - me.verts = None - - + bpy.data.remove_mesh(me) +# me.verts = None # --------------------------- Footer if world: - has_mist = world.mode & 1 - mist_intense, mist_start, mist_end, mist_height = world.mist - world_hor = world.hor + m = world.mist + has_mist = m.enabled +# has_mist = world.mode & 1 + mist_intense = m.intensity + mist_start = m.start + mist_end = m.depth + mist_height = m.height +# mist_intense, mist_start, mist_end, mist_height = world.mist + world_hor = world.horizon_color +# world_hor = world.hor else: has_mist = mist_intense = mist_start = mist_end = mist_height = 0 world_hor = 0,0,0 @@ -2771,17 +3041,19 @@ Takes: {''') # copy images if enabled - if EXP_IMAGE_COPY: - copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) +# if EXP_IMAGE_COPY: +# # copy_images( basepath, [ tex[1] for tex in textures if tex[1] != None ]) +# bpy.util.copy_images( [ tex[1] for tex in textures if tex[1] != None ], basepath) - print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) + print('export finished in %.4f sec.' % (time.clock() - start_time)) +# print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) return True # -------------------------------------------- # UI Function - not a part of the exporter. # this is to seperate the user interface from the rest of the exporter. -from Blender import Draw, Window +# from Blender import Draw, Window EVENT_NONE = 0 EVENT_EXIT = 1 EVENT_REDRAW = 2 @@ -2804,11 +3076,6 @@ def do_obs_sce(e,v): GLOBALS['EXP_OBS_SCENE'].val = 1 GLOBALS['EXP_OBS_SELECTED'].val = 0 -def do_obs_sce(e,v): - GLOBALS['EVENT'] = e - GLOBALS['EXP_OBS_SCENE'].val = 1 - GLOBALS['EXP_OBS_SELECTED'].val = 0 - def do_batch_type_grp(e,v): GLOBALS['EVENT'] = e GLOBALS['BATCH_GROUP'].val = 1 @@ -2837,21 +3104,21 @@ def fbx_ui_exit(e,v): def do_help(e,v): url = 'http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx' - print 'Trying to open web browser with documentation at this address...' - print '\t' + url + print('Trying to open web browser with documentation at this address...') + print('\t' + url) try: import webbrowser webbrowser.open(url) except: Blender.Draw.PupMenu("Error%t|Opening a webbrowser requires a full python installation") - print '...could not open a browser window.' + print('...could not open a browser window.') # run when export is pressed #def fbx_ui_write(e,v): -def fbx_ui_write(filename): +def fbx_ui_write(filename, context): # Dont allow overwriting files when saving normally if not GLOBALS['BATCH_ENABLE'].val: @@ -2874,6 +3141,7 @@ def fbx_ui_write(filename): ret = write(\ filename, None,\ + context, GLOBALS['EXP_OBS_SELECTED'].val,\ GLOBALS['EXP_MESH'].val,\ GLOBALS['EXP_MESH_APPLY_MOD'].val,\ @@ -3071,14 +3339,115 @@ def write_ui(): # GLOBALS.clear() -#test = [write_ui] -if __name__ == '__main__': - # Cant call the file selector first because of a bug in the interface that crashes it. - # Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx')) - #write('/scratch/test.fbx') - #write_ui('/scratch/test.fbx') - - if not set: - Draw.PupMenu('Error%t|A full install of python2.3 or python 2.4+ is needed to run this script.') - else: - write_ui() + +class EXPORT_OT_fbx(bpy.types.Operator): + ''' + Operator documentation text, will be used for the operator tooltip and python docs. + ''' + __idname__ = "export.fbx" + __label__ = "Export FBX" + + # 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 exporting the FBX file", maxlen= 1024, default= ""), + + bpy.props.BoolProperty(attr="EXP_OBS_SELECTED", name="Selected Objects", description="Export selected objects on visible layers", default=True), +# bpy.props.BoolProperty(attr="EXP_OBS_SCENE", name="Scene Objects", description="Export all objects in this scene", default=True), + bpy.props.FloatProperty(attr="_SCALE", name="Scale", description="Scale all data, (Note! some imports dont support scaled armatures)", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=1.0), + bpy.props.BoolProperty(attr="_XROT90", name="Rot X90", description="Rotate all objects 90 degrese about the X axis", default=True), + bpy.props.BoolProperty(attr="_YROT90", name="Rot Y90", description="Rotate all objects 90 degrese about the Y axis", default=False), + bpy.props.BoolProperty(attr="_ZROT90", name="Rot Z90", description="Rotate all objects 90 degrese about the Z axis", default=False), + bpy.props.BoolProperty(attr="EXP_EMPTY", name="Empties", description="Export empty objects", default=True), + bpy.props.BoolProperty(attr="EXP_CAMERA", name="Cameras", description="Export camera objects", default=True), + bpy.props.BoolProperty(attr="EXP_LAMP", name="Lamps", description="Export lamp objects", default=True), + bpy.props.BoolProperty(attr="EXP_ARMATURE", name="Armatures", description="Export armature objects", default=True), + bpy.props.BoolProperty(attr="EXP_MESH", name="Meshes", description="Export mesh objects", default=True), + bpy.props.BoolProperty(attr="EXP_MESH_APPLY_MOD", name="Modifiers", description="Apply modifiers to mesh objects", default=True), + bpy.props.BoolProperty(attr="EXP_MESH_HQ_NORMALS", name="HQ Normals", description="Generate high quality normals", default=True), + bpy.props.BoolProperty(attr="EXP_IMAGE_COPY", name="Copy Image Files", description="Copy image files to the destination path", default=False), + # armature animation + bpy.props.BoolProperty(attr="ANIM_ENABLE", name="Enable Animation", description="Export keyframe animation", default=True), + bpy.props.BoolProperty(attr="ANIM_OPTIMIZE", name="Optimize Keyframes", description="Remove double keyframes", default=True), + bpy.props.FloatProperty(attr="ANIM_OPTIMIZE_PRECISSION", name="Precision", description="Tolerence for comparing double keyframes (higher for greater accuracy)", min=1, max=16, soft_min=1, soft_max=16, default=6.0), +# bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="Current Action", description="Use actions currently applied to the armatures (use scene start/end frame)", default=True), + bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="All Actions", description="Use all actions for armatures, if false, use current action", default=False), + # batch + bpy.props.BoolProperty(attr="BATCH_ENABLE", name="Enable Batch", description="Automate exporting multiple scenes or groups to files", default=False), + bpy.props.BoolProperty(attr="BATCH_GROUP", name="Group > File", description="Export each group as an FBX file, if false, export each scene as an FBX file", default=False), + bpy.props.BoolProperty(attr="BATCH_OWN_DIR", name="Own Dir", description="Create a dir for each exported file", default=True), + bpy.props.StringProperty(attr="BATCH_FILE_PREFIX", name="Prefix", description="Prefix each file with this name", maxlen= 1024, default=""), + ] + + def poll(self, context): + print("Poll") + return context.active_object != None + + def execute(self, context): + if not self.path: + raise Exception("path not set") + + GLOBAL_MATRIX = mtx4_identity + GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = self._SCALE + if self._XROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n + if self._YROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n + if self._ZROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n + + write(self.path, + None, # XXX + context, + self.EXP_OBS_SELECTED, + self.EXP_MESH, + self.EXP_MESH_APPLY_MOD, +# self.EXP_MESH_HQ_NORMALS, + self.EXP_ARMATURE, + self.EXP_LAMP, + self.EXP_CAMERA, + self.EXP_EMPTY, + self.EXP_IMAGE_COPY, + GLOBAL_MATRIX, + self.ANIM_ENABLE, + self.ANIM_OPTIMIZE, + self.ANIM_OPTIMIZE_PRECISSION, + self.ANIM_ACTION_ALL, + self.BATCH_ENABLE, + self.BATCH_GROUP, + self.BATCH_FILE_PREFIX, + self.BATCH_OWN_DIR) + + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + +bpy.ops.add(EXPORT_OT_fbx) + +# if __name__ == "__main__": +# bpy.ops.EXPORT_OT_ply(filename="/tmp/test.ply") + + +# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) +# - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print +# - get rid of cleanName somehow +# + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565 +# + get rid of BPyObject_getObjectArmature, move it in RNA? +# - BATCH_ENABLE and BATCH_GROUP options: line 327 +# - implement all BPyMesh_* used here with RNA +# - getDerivedObjects is not fully replicated with .dupli* funcs +# - talk to Campbell, this code won't work? lines 1867-1875 +# - don't know what those colbits are, do we need them? they're said to be deprecated in DNA_object_types.h: 1886-1893 +# - no hq normals: 1900-1901 + +# TODO + +# - bpy.data.remove_scene: line 366 +# - bpy.sys.time move to bpy.sys.util? +# - new scene creation, activation: lines 327-342, 368 +# - uses bpy.sys.expandpath, *.relpath - replace at least relpath + +# SMALL or COSMETICAL +# - find a way to get blender version, and put it in bpy.util?, old was Blender.Get('version') diff --git a/release/scripts/io/export_obj.py b/release/scripts/io/export_obj.py index 7dffb5d2048..83b400816e3 100644 --- a/release/scripts/io/export_obj.py +++ b/release/scripts/io/export_obj.py @@ -2,14 +2,14 @@ """ Name: 'Wavefront (.obj)...' -Blender: 249 +Blender: 248 Group: 'Export' Tooltip: 'Save a Wavefront OBJ File' """ __author__ = "Campbell Barton, Jiri Hnidek, Paolo Ciccone" __url__ = ['http://wiki.blender.org/index.php/Scripts/Manual/Export/wavefront_obj', 'www.blender.org', 'blenderartists.org'] -__version__ = "1.22" +__version__ = "1.21" __bpydoc__ = """\ This script is an exporter to OBJ file format. @@ -23,11 +23,11 @@ will be exported as mesh data. """ +# -------------------------------------------------------------------------- +# OBJ Export v1.1 by Campbell Barton (AKA Ideasman) +# -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # -# Script copyright (C) Campbell J Barton 2007-2009 -# - V1.22- bspline import/export added (funded by PolyDimensions GmbH) -# # 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 @@ -35,26 +35,27 @@ will be exported as mesh data. # # 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 +# 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. +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- +# import math and other in functions that use them for the sake of fast Blender startup +# import math +import os +import time + +import bpy +import Mathutils -import Blender -from Blender import Mesh, Scene, Window, sys, Image, Draw -import BPyMesh -import BPyObject -import BPySys -import BPyMessages # Returns a tuple - path,extension. -# 'hello.obj' > ('hello', '.obj') +# 'hello.obj' > ('hello', '.obj') def splitExt(path): dotidx = path.rfind('.') if dotidx == -1: @@ -68,23 +69,47 @@ def fixName(name): else: return name.replace(' ', '_') + +# this used to be in BPySys module +# frankly, I don't understand how it works +def BPySys_cleanName(name): + + v = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,58,59,60,61,62,63,64,91,92,93,94,96,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254] + + invalid = ''.join([chr(i) for i in v]) + + for ch in invalid: + name = name.replace(ch, '_') + return name + # A Dict of Materials # (material.name, image.name):matname_imagename # matname_imagename has gaps removed. -MTL_DICT = {} +MTL_DICT = {} + +def write_mtl(scene, filename, copy_images): + + world = scene.world + worldAmb = world.ambient_color + + dest_dir = os.path.dirname(filename) + + def copy_image(image): + rel = image.get_export_path(dest_dir, True) + + if copy_images: + abspath = image.get_export_path(dest_dir, False) + if not os.path.exists(abs_path): + shutil.copy(image.get_abs_filename(), abs_path) + + return rel + -def write_mtl(filename): - - world = Blender.World.GetCurrent() - if world: - worldAmb = world.getAmb() - else: - worldAmb = (0,0,0) # Default value - file = open(filename, "w") - file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) + # XXX +# file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) file.write('# Material Count: %i\n' % len(MTL_DICT)) # Write material/image combinations we have used. - for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems(): + for key, (mtl_mat_name, mat, img) in MTL_DICT.items(): # Get the Blender data for the material and the image. # Having an image named None will make a bug, dont do it :) @@ -92,17 +117,20 @@ def write_mtl(filename): file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname if mat: - file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's - file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour, - file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse - file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.spec for c in mat.specCol]) ) # Specular - file.write('Ni %.6f\n' % mat.IOR) # Refraction index + file.write('Ns %.6f\n' % ((mat.specular_hardness-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's + file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.ambient for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.diffuse_intensity for c in mat.diffuse_color]) ) # Diffuse + file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.specular_intensity for c in mat.specular_color]) ) # Specular + if hasattr(mat, "ior"): + file.write('Ni %.6f\n' % mat.ior) # Refraction index + else: + file.write('Ni %.6f\n' % 1.0) file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) - + # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. - if mat.getMode() & Blender.Material.Modes['SHADELESS']: + if mat.shadeless: file.write('illum 0\n') # ignore lighting - elif mat.getSpec() == 0: + elif mat.specular_intensity == 0: file.write('illum 1\n') # no specular. else: file.write('illum 2\n') # light normaly @@ -110,21 +138,25 @@ def write_mtl(filename): else: #write a dummy material here? file.write('Ns 0\n') - file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd 0.8 0.8 0.8\n') file.write('Ks 0.8 0.8 0.8\n') file.write('d 1\n') # No alpha file.write('illum 2\n') # light normaly # Write images! - if img: # We have an image on the face! - file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image + if img: # We have an image on the face! + # write relative image path + rel = copy_image(img) + file.write('map_Kd %s\n' % rel) # Diffuse mapping image +# file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image elif mat: # No face image. if we havea material search for MTex image. - for mtex in mat.getTextures(): - if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: + for mtex in mat.textures: + if mtex and mtex.texture.type == 'IMAGE': try: - filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1] + filename = copy_image(mtex.texture.image) +# filename = mtex.texture.image.filename.split('\\')[-1].split('/')[-1] file.write('map_Kd %s\n' % filename) # Diffuse mapping image break except: @@ -135,6 +167,7 @@ def write_mtl(filename): file.close() +# XXX not used def copy_file(source, dest): file = open(source, 'rb') data = file.read() @@ -145,22 +178,25 @@ def copy_file(source, dest): file.close() +# XXX not used def copy_images(dest_dir): - if dest_dir[-1] != sys.sep: - dest_dir += sys.sep + if dest_dir[-1] != os.sep: + dest_dir += os.sep +# if dest_dir[-1] != sys.sep: +# dest_dir += sys.sep # Get unique image names uniqueImages = {} - for matname, mat, image in MTL_DICT.itervalues(): # Only use image name + for matname, mat, image in MTL_DICT.values(): # Only use image name # Get Texface images if image: uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default. # Get MTex images if mat: - for mtex in mat.getTextures(): - if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: - image_tex = mtex.tex.image + for mtex in mat.textures: + if mtex and mtex.texture.type == 'IMAGE': + image_tex = mtex.texture.image if image_tex: try: uniqueImages[image_tex] = image_tex @@ -170,18 +206,22 @@ def copy_images(dest_dir): # Now copy images copyCount = 0 - for bImage in uniqueImages.itervalues(): - image_path = sys.expandpath(bImage.filename) - if sys.exists(image_path): - # Make a name for the target path. - dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] - if not sys.exists(dest_image_path): # Image isnt alredy there - print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) - copy_file(image_path, dest_image_path) - copyCount+=1 - print '\tCopied %d images' % copyCount +# for bImage in uniqueImages.values(): +# image_path = bpy.sys.expandpath(bImage.filename) +# if bpy.sys.exists(image_path): +# # Make a name for the target path. +# dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] +# if not bpy.sys.exists(dest_image_path): # Image isnt alredy there +# print('\tCopying "%s" > "%s"' % (image_path, dest_image_path)) +# copy_file(image_path, dest_image_path) +# copyCount+=1 + +# paths= bpy.util.copy_images(uniqueImages.values(), dest_dir) + print('\tCopied %d images' % copyCount) +# print('\tCopied %d images' % copyCount) +# XXX not converted def test_nurbs_compat(ob): if ob.type != 'Curve': return False @@ -192,6 +232,8 @@ def test_nurbs_compat(ob): return False + +# XXX not converted def write_nurb(file, ob, ob_mat): tot_verts = 0 cu = ob.data @@ -204,15 +246,15 @@ def write_nurb(file, ob, ob_mat): else: DEG_ORDER_U = nu.orderU-1 # Tested to be correct if nu.type==1: - print "\tWarning, bezier curve:", ob.name, "only poly and nurbs curves supported" + print("\tWarning, bezier curve:", ob.name, "only poly and nurbs curves supported") continue if nu.knotsV: - print "\tWarning, surface:", ob.name, "only poly and nurbs curves supported" + print("\tWarning, surface:", ob.name, "only poly and nurbs curves supported") continue if len(nu) <= DEG_ORDER_U: - print "\tWarning, orderU is lower then vert count, skipping:", ob.name + print("\tWarning, orderU is lower then vert count, skipping:", ob.name) continue pt_num = 0 @@ -229,7 +271,7 @@ def write_nurb(file, ob, ob_mat): file.write('cstype bspline\n') # not ideal, hard coded file.write('deg %d\n' % DEG_ORDER_U) # not used for curves but most files have it still - curve_ls = [-(i+1) for i in xrange(pt_num)] + curve_ls = [-(i+1) for i in range(pt_num)] # 'curv' keyword if do_closed: @@ -245,10 +287,10 @@ def write_nurb(file, ob, ob_mat): # 'parm' keyword tot_parm = (DEG_ORDER_U + 1) + pt_num tot_parm_div = float(tot_parm-1) - parm_ls = [(i/tot_parm_div) for i in xrange(tot_parm)] + parm_ls = [(i/tot_parm_div) for i in range(tot_parm)] if do_endpoints: # end points, force param - for i in xrange(DEG_ORDER_U+1): + for i in range(DEG_ORDER_U+1): parm_ls[i] = 0.0 parm_ls[-(1+i)] = 1.0 @@ -258,24 +300,38 @@ def write_nurb(file, ob, ob_mat): return tot_verts -def write(filename, objects,\ -EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False,\ -EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False,\ -EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True,\ -EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=False,\ -EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): +def write(filename, objects, scene, + EXPORT_TRI=False, + EXPORT_EDGES=False, + EXPORT_NORMALS=False, + EXPORT_NORMALS_HQ=False, + EXPORT_UV=True, + EXPORT_MTL=True, + EXPORT_COPY_IMAGES=False, + EXPORT_APPLY_MODIFIERS=True, + EXPORT_ROTX90=True, + EXPORT_BLEN_OBS=True, + EXPORT_GROUP_BY_OB=False, + EXPORT_GROUP_BY_MAT=False, + EXPORT_KEEP_VERT_ORDER=False, + EXPORT_POLYGROUPS=False, + EXPORT_CURVE_AS_NURBS=True): ''' Basic write function. The context and options must be alredy set This can be accessed externaly eg. write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. ''' + + # XXX + import math def veckey3d(v): return round(v.x, 6), round(v.y, 6), round(v.z, 6) def veckey2d(v): - return round(v.x, 6), round(v.y, 6) + return round(v[0], 6), round(v[1], 6) + # return round(v.x, 6), round(v.y, 6) def findVertexGroupName(face, vWeightMap): """ @@ -287,29 +343,44 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): of vertices is the face's group """ weightDict = {} - for vert in face: - vWeights = vWeightMap[vert.index] + for vert_index in face.verts: +# for vert in face: + vWeights = vWeightMap[vert_index] +# vWeights = vWeightMap[vert] for vGroupName, weight in vWeights: weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight if weightDict: - alist = [(weight,vGroupName) for vGroupName, weight in weightDict.iteritems()] # sort least to greatest amount of weight + alist = [(weight,vGroupName) for vGroupName, weight in weightDict.items()] # sort least to greatest amount of weight alist.sort() return(alist[-1][1]) # highest value last else: return '(null)' + # TODO: implement this in C? dunno how it should be called... + def getVertsFromGroup(me, group_index): + ret = [] + + for i, v in enumerate(me.verts): + for g in v.groups: + if g.group == group_index: + ret.append((i, g.weight)) + + return ret - print 'OBJ Export path: "%s"' % filename + + print('OBJ Export path: "%s"' % filename) temp_mesh_name = '~tmp-mesh' - time1 = sys.time() - scn = Scene.GetCurrent() + time1 = time.clock() +# time1 = sys.time() +# scn = Scene.GetCurrent() file = open(filename, "w") - + # Write Header - file.write('# Blender3D v%s OBJ File: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] )) + version = "2.5" + file.write('# Blender3D v%s OBJ File: %s\n' % (version, bpy.data.filename.split('/')[-1].split('\\')[-1] )) file.write('# www.blender3d.org\n') # Tell the obj file what material file to use. @@ -317,107 +388,125 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1]) file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] )) - # Get the container mesh. - used for applying modifiers and non mesh objects. - containerMesh = meshName = tempMesh = None - for meshName in Blender.NMesh.GetNames(): - if meshName.startswith(temp_mesh_name): - tempMesh = Mesh.Get(meshName) - if not tempMesh.users: - containerMesh = tempMesh - if not containerMesh: - containerMesh = Mesh.New(temp_mesh_name) - if EXPORT_ROTX90: - mat_xrot90= Blender.Mathutils.RotationMatrix(-90, 4, 'x') + mat_xrot90= Mathutils.RotationMatrix(-math.pi/2, 4, 'x') - del meshName - del tempMesh - # Initialize totals, these are updated each object totverts = totuvco = totno = 1 face_vert_index = 1 globalNormals = {} - + # Get all meshes for ob_main in objects: - for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): + + # ignore dupli children + if ob_main.parent and ob_main.parent.dupli_type != 'NONE': + # XXX + print(ob_main.name, 'is a dupli child - ignoring') + continue + + obs = [] + if ob_main.dupli_type != 'NONE': + # XXX + print('creating dupli_list on', ob_main.name) + ob_main.create_dupli_list() - # Nurbs curve support - if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): - if EXPORT_ROTX90: - ob_mat = ob_mat * mat_xrot90 + obs = [(dob.object, dob.matrix) for dob in ob_main.dupli_list] + + # XXX debug print + print(ob_main.name, 'has', len(obs), 'dupli children') + else: + obs = [(ob_main, ob_main.matrix)] + + for ob, ob_mat in obs: + + # XXX postponed +# # Nurbs curve support +# if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): +# if EXPORT_ROTX90: +# ob_mat = ob_mat * mat_xrot90 - totverts += write_nurb(file, ob, ob_mat) +# totverts += write_nurb(file, ob, ob_mat) +# continue +# end nurbs + + if ob.type != 'MESH': continue - # end nurbs - - # Will work for non meshes now! :) - # getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None) - me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) - if not me: - continue - + + me = ob.create_mesh(EXPORT_APPLY_MODIFIERS, 'PREVIEW') + + if EXPORT_ROTX90: + me.transform(ob_mat * mat_xrot90) + else: + me.transform(ob_mat) + +# # Will work for non meshes now! :) +# me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn) +# if not me: +# continue + if EXPORT_UV: - faceuv= me.faceUV + faceuv = len(me.uv_textures) > 0 else: faceuv = False - + + # XXX - todo, find a better way to do triangulation + # ...removed convert_to_triface because it relies on editmesh + ''' # We have a valid mesh if EXPORT_TRI and me.faces: # Add a dummy object to it. has_quads = False for f in me.faces: - if len(f) == 4: + if f.verts[3] != 0: has_quads = True break if has_quads: - oldmode = Mesh.Mode() - Mesh.Mode(Mesh.SelectModes['FACE']) - - me.sel = True - tempob = scn.objects.new(me) - me.quadToTriangle(0) # more=0 shortest length - oldmode = Mesh.Mode(oldmode) - scn.objects.unlink(tempob) - - Mesh.Mode(oldmode) + newob = bpy.data.add_object('MESH', 'temp_object') + newob.data = me + # if we forget to set Object.data - crash + scene.add_object(newob) + newob.convert_to_triface(scene) + # mesh will still be there + scene.remove_object(newob) + ''' # Make our own list so it can be sorted to reduce context switching - faces = [ f for f in me.faces ] + face_index_pairs = [ (face, index) for index, face in enumerate(me.faces)] + # faces = [ f for f in me.faces ] if EXPORT_EDGES: edges = me.edges else: edges = [] - - if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write + + if not (len(face_index_pairs)+len(edges)+len(me.verts)): # Make sure there is somthing to write + + # clean up + bpy.data.remove_mesh(me) + continue # dont bother with this mesh. - if EXPORT_ROTX90: - me.transform(ob_mat*mat_xrot90) - else: - me.transform(ob_mat) - + # XXX # High Quality Normals - if EXPORT_NORMALS and faces: - if EXPORT_NORMALS_HQ: - BPyMesh.meshCalcNormals(me) - else: - # transforming normals is incorrect - # when the matrix is scaled, - # better to recalculate them - me.calcNormals() + if EXPORT_NORMALS and face_index_pairs: + me.calc_normals() +# if EXPORT_NORMALS_HQ: +# BPyMesh.meshCalcNormals(me) +# else: +# # transforming normals is incorrect +# # when the matrix is scaled, +# # better to recalculate them +# me.calcNormals() - # # Crash Blender - #materials = me.getMaterials(1) # 1 == will return None in the list. materials = me.materials materialNames = [] - materialItems = materials[:] + materialItems = [m for m in materials] if materials: for mat in materials: if mat: # !=None @@ -437,15 +526,41 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_KEEP_VERT_ORDER: pass elif faceuv: - try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) - except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) + # XXX update + tface = me.active_uv_texture.data + + # exception only raised if Python 2.3 or lower... + try: + face_index_pairs.sort(key = lambda a: (a[0].material_index, tface[a[1]].image, a[0].smooth)) + except: + face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, tface[a[1]].image, a[0].smooth), + (b[0].material_index, tface[b[1]].image, b[0].smooth))) elif len(materials) > 1: - try: faces.sort(key = lambda a: (a.mat, a.smooth)) - except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) + try: + face_index_pairs.sort(key = lambda a: (a[0].material_index, a[0].smooth)) + except: + face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, a[0].smooth), + (b[0].material_index, b[0].smooth))) else: # no materials - try: faces.sort(key = lambda a: a.smooth) - except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) + try: + face_index_pairs.sort(key = lambda a: a[0].smooth) + except: + face_index_pairs.sort(lambda a,b: cmp(a[0].smooth, b[0].smooth)) +# if EXPORT_KEEP_VERT_ORDER: +# pass +# elif faceuv: +# try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) +# except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) +# elif len(materials) > 1: +# try: faces.sort(key = lambda a: (a.mat, a.smooth)) +# except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) +# else: +# # no materials +# try: faces.sort(key = lambda a: a.smooth) +# except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) + + faces = [pair[0] for pair in face_index_pairs] # Set the default mat to no material and no image. contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. @@ -453,7 +568,7 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB: name1 = ob.name - name2 = ob.getData(1) + name2 = ob.data.name if name1 == name2: obnamestring = fixName(name1) else: @@ -472,20 +587,41 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): # UV if faceuv: uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ - + uv_dict = {} # could use a set() here - for f_index, f in enumerate(faces): - - for uv_index, uv in enumerate(f.uv): + uv_layer = me.active_uv_texture + for f, f_index in face_index_pairs: + + tface = uv_layer.data[f_index] + + uvs = tface.uv + # uvs = [tface.uv1, tface.uv2, tface.uv3] + + # # add another UV if it's a quad + # if len(f.verts) == 4: + # uvs.append(tface.uv4) + + for uv_index, uv in enumerate(uvs): uvkey = veckey2d(uv) try: uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] except: uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) file.write('vt %.6f %.6f\n' % tuple(uv)) + +# uv_dict = {} # could use a set() here +# for f_index, f in enumerate(faces): + +# for uv_index, uv in enumerate(f.uv): +# uvkey = veckey2d(uv) +# try: +# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] +# except: +# uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) +# file.write('vt %.6f %.6f\n' % tuple(uv)) uv_unique_count = len(uv_dict) - del uv, uvkey, uv_dict, f_index, uv_index +# del uv, uvkey, uv_dict, f_index, uv_index # Only need uv_unique_count and uv_face_mapping # NORMAL, Smooth/Non smoothed. @@ -493,55 +629,81 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): for f in faces: if f.smooth: for v in f: - noKey = veckey3d(v.no) - if not globalNormals.has_key( noKey ): + noKey = veckey3d(v.normal) + if noKey not in globalNormals: globalNormals[noKey] = totno totno +=1 file.write('vn %.6f %.6f %.6f\n' % noKey) else: # Hard, 1 normal from the face. - noKey = veckey3d(f.no) - if not globalNormals.has_key( noKey ): + noKey = veckey3d(f.normal) + if noKey not in globalNormals: globalNormals[noKey] = totno totno +=1 file.write('vn %.6f %.6f %.6f\n' % noKey) if not faceuv: f_image = None - + + # XXX if EXPORT_POLYGROUPS: # Retrieve the list of vertex groups - vertGroupNames = me.getVertGroupNames() +# vertGroupNames = me.getVertGroupNames() currentVGroup = '' # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to - vgroupsMap = [[] for _i in xrange(len(me.verts))] - for vertexGroupName in vertGroupNames: - for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): - vgroupsMap[vIdx].append((vertexGroupName, vWeight)) + vgroupsMap = [[] for _i in range(len(me.verts))] +# vgroupsMap = [[] for _i in xrange(len(me.verts))] + for g in ob.vertex_groups: +# for vertexGroupName in vertGroupNames: + for vIdx, vWeight in getVertsFromGroup(me, g.index): +# for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1): + vgroupsMap[vIdx].append((g.name, vWeight)) for f_index, f in enumerate(faces): - f_v= f.v + f_v = [{"index": index, "vertex": me.verts[index]} for index in f.verts] + + # if f.verts[3] == 0: + # f_v.pop() + +# f_v= f.v f_smooth= f.smooth - f_mat = min(f.mat, len(materialNames)-1) + f_mat = min(f.material_index, len(materialNames)-1) +# f_mat = min(f.mat, len(materialNames)-1) if faceuv: - f_image = f.image - f_uv= f.uv + + tface = me.active_uv_texture.data[face_index_pairs[f_index][1]] + + f_image = tface.image + f_uv = tface.uv + # f_uv= [tface.uv1, tface.uv2, tface.uv3] + # if len(f.verts) == 4: + # f_uv.append(tface.uv4) +# f_image = f.image +# f_uv= f.uv # MAKE KEY if faceuv and f_image: # Object is always true. - key = materialNames[f_mat], f_image.name + key = materialNames[f_mat], f_image.name else: - key = materialNames[f_mat], None # No image, use None instead. - + key = materialNames[f_mat], None # No image, use None instead. + # Write the vertex group if EXPORT_POLYGROUPS: - if vertGroupNames: + if len(ob.vertex_groups): # find what vertext group the face belongs to theVGroup = findVertexGroupName(f,vgroupsMap) if theVGroup != currentVGroup: currentVGroup = theVGroup file.write('g %s\n' % theVGroup) +# # Write the vertex group +# if EXPORT_POLYGROUPS: +# if vertGroupNames: +# # find what vertext group the face belongs to +# theVGroup = findVertexGroupName(f,vgroupsMap) +# if theVGroup != currentVGroup: +# currentVGroup = theVGroup +# file.write('g %s\n' % theVGroup) # CHECK FOR CONTEXT SWITCH if key == contextMat: @@ -550,7 +712,8 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if key[0] == None and key[1] == None: # Write a null material, since we know the context has changed. if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) + # can be mat_image or (null) + file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.data.name)) ) # can be mat_image or (null) file.write('usemtl (null)\n') # mat, image else: @@ -569,7 +732,7 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) + file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.data.name), mat_data[0]) ) # can be mat_image or (null) file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) @@ -587,23 +750,22 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_NORMALS: if f_smooth: # Smoothed, use vertex normals for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % (\ - v.index+totverts,\ - totuvco + uv_face_mapping[f_index][vi],\ - globalNormals[ veckey3d(v.no) ])) # vert, uv, normal + file.write( ' %d/%d/%d' % \ + (v["index"] + totverts, + totuvco + uv_face_mapping[f_index][vi], + globalNormals[ veckey3d(v["vertex"].normal) ]) ) # vert, uv, normal else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.no) ] + no = globalNormals[ veckey3d(f.normal) ] for vi, v in enumerate(f_v): - file.write( ' %d/%d/%d' % (\ - v.index+totverts,\ - totuvco + uv_face_mapping[f_index][vi],\ - no)) # vert, uv, normal - + file.write( ' %d/%d/%d' % \ + (v["index"] + totverts, + totuvco + uv_face_mapping[f_index][vi], + no) ) # vert, uv, normal else: # No Normals for vi, v in enumerate(f_v): file.write( ' %d/%d' % (\ - v.index+totverts,\ + v["index"] + totverts,\ totuvco + uv_face_mapping[f_index][vi])) # vert, uv face_vert_index += len(f_v) @@ -612,290 +774,105 @@ EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True): if EXPORT_NORMALS: if f_smooth: # Smoothed, use vertex normals for v in f_v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - globalNormals[ veckey3d(v.no) ])) + file.write( ' %d//%d' % + (v["index"] + totverts, globalNormals[ veckey3d(v["vertex"].normal) ]) ) else: # No smoothing, face normals - no = globalNormals[ veckey3d(f.no) ] + no = globalNormals[ veckey3d(f.normal) ] for v in f_v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - no)) + file.write( ' %d//%d' % (v["index"] + totverts, no) ) else: # No Normals for v in f_v: - file.write( ' %d' % (\ - v.index+totverts)) + file.write( ' %d' % (v["index"] + totverts) ) file.write('\n') # Write edges. if EXPORT_EDGES: - LOOSE= Mesh.EdgeFlags.LOOSE for ed in edges: - if ed.flag & LOOSE: - file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts)) + if ed.loose: + file.write('f %d %d\n' % (ed.verts[0] + totverts, ed.verts[1] + totverts)) # Make the indicies global rather then per mesh totverts += len(me.verts) if faceuv: totuvco += uv_unique_count - me.verts= None + + # clean up + bpy.data.remove_mesh(me) + + if ob_main.dupli_type != 'NONE': + ob_main.free_dupli_list() + file.close() # Now we have all our materials, save them if EXPORT_MTL: - write_mtl(mtlfilename) - if EXPORT_COPY_IMAGES: - dest_dir = filename - # Remove chars until we are just the path. - while dest_dir and dest_dir[-1] not in '\\/': - dest_dir = dest_dir[:-1] - if dest_dir: - copy_images(dest_dir) - else: - print '\tError: "%s" could not be used as a base for an image path.' % filename - - print "OBJ Export time: %.2f" % (sys.time() - time1) - - - -def write_ui(filename): - - if not filename.lower().endswith('.obj'): - filename += '.obj' - - if not BPyMessages.Warning_SaveOver(filename): - return - - global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\ - EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\ - EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ - EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\ - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\ - EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS - - EXPORT_APPLY_MODIFIERS = Draw.Create(0) - EXPORT_ROTX90 = Draw.Create(1) - EXPORT_TRI = Draw.Create(0) - EXPORT_EDGES = Draw.Create(1) - EXPORT_NORMALS = Draw.Create(0) - EXPORT_NORMALS_HQ = Draw.Create(0) - EXPORT_UV = Draw.Create(1) - EXPORT_MTL = Draw.Create(1) - EXPORT_SEL_ONLY = Draw.Create(1) - EXPORT_ALL_SCENES = Draw.Create(0) - EXPORT_ANIMATION = Draw.Create(0) - EXPORT_COPY_IMAGES = Draw.Create(0) - EXPORT_BLEN_OBS = Draw.Create(0) - EXPORT_GROUP_BY_OB = Draw.Create(0) - EXPORT_GROUP_BY_MAT = Draw.Create(0) - EXPORT_KEEP_VERT_ORDER = Draw.Create(1) - EXPORT_POLYGROUPS = Draw.Create(0) - EXPORT_CURVE_AS_NURBS = Draw.Create(1) - - - # Old UI - ''' - # removed too many options are bad! - - # Get USER Options - pup_block = [\ - ('Context...'),\ - ('Selection Only', EXPORT_SEL_ONLY, 'Only export objects in visible selection. Else export whole scene.'),\ - ('All Scenes', EXPORT_ALL_SCENES, 'Each scene as a separate OBJ file.'),\ - ('Animation', EXPORT_ANIMATION, 'Each frame as a numbered OBJ file.'),\ - ('Object Prefs...'),\ - ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\ - ('Rotate X90', EXPORT_ROTX90 , 'Rotate on export so Blenders UP is translated into OBJs UP'),\ - ('Keep Vert Order', EXPORT_KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\ - ('Extra Data...'),\ - ('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\ - ('Normals', EXPORT_NORMALS, 'Export vertex normal data (Ignored on import).'),\ - ('High Quality Normals', EXPORT_NORMALS_HQ, 'Calculate high quality normals for rendering.'),\ - ('UVs', EXPORT_UV, 'Export texface UV coords.'),\ - ('Materials', EXPORT_MTL, 'Write a separate MTL file with the OBJ.'),\ - ('Copy Images', EXPORT_COPY_IMAGES, 'Copy image files to the export directory, never overwrite.'),\ - ('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\ - ('Grouping...'),\ - ('Objects', EXPORT_BLEN_OBS, 'Export blender objects as "OBJ objects".'),\ - ('Object Groups', EXPORT_GROUP_BY_OB, 'Export blender objects as "OBJ Groups".'),\ - ('Material Groups', EXPORT_GROUP_BY_MAT, 'Group by materials.'),\ - ] - - if not Draw.PupBlock('Export...', pup_block): - return - ''' - - # BEGIN ALTERNATIVE UI ******************* - if True: - - EVENT_NONE = 0 - EVENT_EXIT = 1 - EVENT_REDRAW = 2 - EVENT_EXPORT = 3 - - GLOBALS = {} - GLOBALS['EVENT'] = EVENT_REDRAW - #GLOBALS['MOUSE'] = Window.GetMouseCoords() - GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] - - def obj_ui_set_event(e,v): - GLOBALS['EVENT'] = e - - def do_split(e,v): - global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER, EXPORT_POLYGROUPS - if EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val: - EXPORT_KEEP_VERT_ORDER.val = 0 - else: - EXPORT_KEEP_VERT_ORDER.val = 1 - - def do_vertorder(e,v): - global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER - if EXPORT_KEEP_VERT_ORDER.val: - EXPORT_BLEN_OBS.val = EXPORT_GROUP_BY_OB.val = EXPORT_GROUP_BY_MAT.val = EXPORT_APPLY_MODIFIERS.val = 0 - else: - if not (EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val): - EXPORT_KEEP_VERT_ORDER.val = 1 - - - def do_help(e,v): - url = __url__[0] - print 'Trying to open web browser with documentation at this address...' - print '\t' + url - - try: - import webbrowser - webbrowser.open(url) - except: - print '...could not open a browser window.' - - def obj_ui(): - ui_x, ui_y = GLOBALS['MOUSE'] - - # Center based on overall pup size - ui_x -= 165 - ui_y -= 140 - - global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\ - EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\ - EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ - EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\ - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\ - EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS - - Draw.Label('Context...', ui_x+9, ui_y+239, 220, 20) - Draw.BeginAlign() - EXPORT_SEL_ONLY = Draw.Toggle('Selection Only', EVENT_NONE, ui_x+9, ui_y+219, 110, 20, EXPORT_SEL_ONLY.val, 'Only export objects in visible selection. Else export whole scene.') - EXPORT_ALL_SCENES = Draw.Toggle('All Scenes', EVENT_NONE, ui_x+119, ui_y+219, 110, 20, EXPORT_ALL_SCENES.val, 'Each scene as a separate OBJ file.') - EXPORT_ANIMATION = Draw.Toggle('Animation', EVENT_NONE, ui_x+229, ui_y+219, 110, 20, EXPORT_ANIMATION.val, 'Each frame as a numbered OBJ file.') - Draw.EndAlign() - - - Draw.Label('Output Options...', ui_x+9, ui_y+189, 220, 20) - Draw.BeginAlign() - EXPORT_APPLY_MODIFIERS = Draw.Toggle('Apply Modifiers', EVENT_REDRAW, ui_x+9, ui_y+170, 110, 20, EXPORT_APPLY_MODIFIERS.val, 'Use transformed mesh data from each object. May break vert order for morph targets.', do_split) - EXPORT_ROTX90 = Draw.Toggle('Rotate X90', EVENT_NONE, ui_x+119, ui_y+170, 110, 20, EXPORT_ROTX90.val, 'Rotate on export so Blenders UP is translated into OBJs UP') - EXPORT_COPY_IMAGES = Draw.Toggle('Copy Images', EVENT_NONE, ui_x+229, ui_y+170, 110, 20, EXPORT_COPY_IMAGES.val, 'Copy image files to the export directory, never overwrite.') - Draw.EndAlign() - - - Draw.Label('Export...', ui_x+9, ui_y+139, 220, 20) - Draw.BeginAlign() - EXPORT_EDGES = Draw.Toggle('Edges', EVENT_NONE, ui_x+9, ui_y+120, 50, 20, EXPORT_EDGES.val, 'Edges not connected to faces.') - EXPORT_TRI = Draw.Toggle('Triangulate', EVENT_NONE, ui_x+59, ui_y+120, 70, 20, EXPORT_TRI.val, 'Triangulate quads.') - Draw.EndAlign() - Draw.BeginAlign() - EXPORT_MTL = Draw.Toggle('Materials', EVENT_NONE, ui_x+139, ui_y+120, 70, 20, EXPORT_MTL.val, 'Write a separate MTL file with the OBJ.') - EXPORT_UV = Draw.Toggle('UVs', EVENT_NONE, ui_x+209, ui_y+120, 31, 20, EXPORT_UV.val, 'Export texface UV coords.') - Draw.EndAlign() - Draw.BeginAlign() - EXPORT_NORMALS = Draw.Toggle('Normals', EVENT_NONE, ui_x+250, ui_y+120, 59, 20, EXPORT_NORMALS.val, 'Export vertex normal data (Ignored on import).') - EXPORT_NORMALS_HQ = Draw.Toggle('HQ', EVENT_NONE, ui_x+309, ui_y+120, 31, 20, EXPORT_NORMALS_HQ.val, 'Calculate high quality normals for rendering.') - Draw.EndAlign() - EXPORT_POLYGROUPS = Draw.Toggle('Polygroups', EVENT_REDRAW, ui_x+9, ui_y+95, 120, 20, EXPORT_POLYGROUPS.val, 'Export vertex groups as OBJ groups (one group per face approximation).') - - EXPORT_CURVE_AS_NURBS = Draw.Toggle('Nurbs', EVENT_NONE, ui_x+139, ui_y+95, 100, 20, EXPORT_CURVE_AS_NURBS.val, 'Export 3D nurbs curves and polylines as OBJ curves, (bezier not supported).') - - - Draw.Label('Blender Objects as OBJ:', ui_x+9, ui_y+59, 220, 20) - Draw.BeginAlign() - EXPORT_BLEN_OBS = Draw.Toggle('Objects', EVENT_REDRAW, ui_x+9, ui_y+39, 60, 20, EXPORT_BLEN_OBS.val, 'Export blender objects as "OBJ objects".', do_split) - EXPORT_GROUP_BY_OB = Draw.Toggle('Groups', EVENT_REDRAW, ui_x+69, ui_y+39, 60, 20, EXPORT_GROUP_BY_OB.val, 'Export blender objects as "OBJ Groups".', do_split) - EXPORT_GROUP_BY_MAT = Draw.Toggle('Material Groups', EVENT_REDRAW, ui_x+129, ui_y+39, 100, 20, EXPORT_GROUP_BY_MAT.val, 'Group by materials.', do_split) - Draw.EndAlign() - - EXPORT_KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+239, ui_y+39, 100, 20, EXPORT_KEEP_VERT_ORDER.val, 'Keep vert and face order, disables some other options. Use for morph targets.', do_vertorder) - - Draw.BeginAlign() - Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 20, 'Load the wiki page for this script', do_help) - Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 20, '', obj_ui_set_event) - Draw.PushButton('Export', EVENT_EXPORT, ui_x+229, ui_y+9, 110, 20, 'Export with these settings', obj_ui_set_event) - Draw.EndAlign() + write_mtl(scene, mtlfilename, EXPORT_COPY_IMAGES) +# if EXPORT_COPY_IMAGES: +# dest_dir = os.path.basename(filename) +# # dest_dir = filename +# # # Remove chars until we are just the path. +# # while dest_dir and dest_dir[-1] not in '\\/': +# # dest_dir = dest_dir[:-1] +# if dest_dir: +# copy_images(dest_dir) +# else: +# print('\tError: "%s" could not be used as a base for an image path.' % filename) + + print("OBJ Export time: %.2f" % (time.clock() - time1)) +# print "OBJ Export time: %.2f" % (sys.time() - time1) + +def do_export(filename, context, + EXPORT_APPLY_MODIFIERS = True, # not used + EXPORT_ROTX90 = True, # wrong + EXPORT_TRI = False, # ok + EXPORT_EDGES = False, + EXPORT_NORMALS = False, # not yet + EXPORT_NORMALS_HQ = False, # not yet + EXPORT_UV = True, # ok + EXPORT_MTL = True, + EXPORT_SEL_ONLY = True, # ok + EXPORT_ALL_SCENES = False, # XXX not working atm + EXPORT_ANIMATION = False, + EXPORT_COPY_IMAGES = False, + EXPORT_BLEN_OBS = True, + EXPORT_GROUP_BY_OB = False, + EXPORT_GROUP_BY_MAT = False, + EXPORT_KEEP_VERT_ORDER = False, + EXPORT_POLYGROUPS = False, + EXPORT_CURVE_AS_NURBS = True): + # Window.EditMode(0) + # Window.WaitCursor(1) - - # hack so the toggle buttons redraw. this is not nice at all - while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_EXPORT): - Draw.UIBlock(obj_ui, 0) - - if GLOBALS['EVENT'] != EVENT_EXPORT: - return - - # END ALTERNATIVE UI ********************* - - - if EXPORT_KEEP_VERT_ORDER.val: - EXPORT_BLEN_OBS.val = False - EXPORT_GROUP_BY_OB.val = False - EXPORT_GROUP_BY_MAT.val = False - EXPORT_APPLY_MODIFIERS.val = False - - Window.EditMode(0) - Window.WaitCursor(1) - - EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val - EXPORT_ROTX90 = EXPORT_ROTX90.val - EXPORT_TRI = EXPORT_TRI.val - EXPORT_EDGES = EXPORT_EDGES.val - EXPORT_NORMALS = EXPORT_NORMALS.val - EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ.val - EXPORT_UV = EXPORT_UV.val - EXPORT_MTL = EXPORT_MTL.val - EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val - EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val - EXPORT_ANIMATION = EXPORT_ANIMATION.val - EXPORT_COPY_IMAGES = EXPORT_COPY_IMAGES.val - EXPORT_BLEN_OBS = EXPORT_BLEN_OBS.val - EXPORT_GROUP_BY_OB = EXPORT_GROUP_BY_OB.val - EXPORT_GROUP_BY_MAT = EXPORT_GROUP_BY_MAT.val - EXPORT_KEEP_VERT_ORDER = EXPORT_KEEP_VERT_ORDER.val - EXPORT_POLYGROUPS = EXPORT_POLYGROUPS.val - EXPORT_CURVE_AS_NURBS = EXPORT_CURVE_AS_NURBS.val - - base_name, ext = splitExt(filename) - context_name = [base_name, '', '', ext] # basename, scene_name, framenumber, extension - - # Use the options to export the data using write() - # def write(filename, objects, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True): - orig_scene = Scene.GetCurrent() - if EXPORT_ALL_SCENES: - export_scenes = Scene.Get() - else: - export_scenes = [orig_scene] + context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension + orig_scene = context.scene + +# if EXPORT_ALL_SCENES: +# export_scenes = bpy.data.scenes +# else: +# export_scenes = [orig_scene] + + # XXX only exporting one scene atm since changing + # current scene is not possible. + # Brecht says that ideally in 2.5 we won't need such a function, + # allowing multiple scenes open at once. + export_scenes = [orig_scene] + # Export all scenes. for scn in export_scenes: - scn.makeCurrent() # If alredy current, this is not slow. - context = scn.getRenderingContext() - orig_frame = Blender.Get('curframe') + # scn.makeCurrent() # If already current, this is not slow. + # context = scn.getRenderingContext() + orig_frame = scn.current_frame if EXPORT_ALL_SCENES: # Add scene name into the context_name - context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. + context_name[1] = '_%s' % BPySys_cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. # Export an animation? if EXPORT_ANIMATION: - scene_frames = xrange(context.startFrame(), context.endFrame()+1) # up to and including the end frame. + scene_frames = range(scn.start_frame, context.end_frame+1) # Up to and including the end frame. else: scene_frames = [orig_frame] # Dont export an animation. @@ -904,9 +881,9 @@ def write_ui(filename): if EXPORT_ANIMATION: # Add frame to the filename. context_name[2] = '_%.6d' % frame - Blender.Set('curframe', frame) + scn.current_frame = frame if EXPORT_SEL_ONLY: - export_objects = scn.objects.context + export_objects = context.selected_objects else: export_objects = scn.objects @@ -914,20 +891,106 @@ def write_ui(filename): # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. # EXPORT THE FILE. - write(full_path, export_objects,\ - EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS,\ - EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL,\ - EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS,\ - EXPORT_ROTX90, EXPORT_BLEN_OBS,\ - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\ - EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS) + write(full_path, export_objects, scn, + EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS, + EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL, + EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS, + EXPORT_ROTX90, EXPORT_BLEN_OBS, + EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER, + EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS) + - Blender.Set('curframe', orig_frame) + scn.current_frame = orig_frame # Restore old active scene. - orig_scene.makeCurrent() - Window.WaitCursor(0) +# orig_scene.makeCurrent() +# Window.WaitCursor(0) -if __name__ == '__main__': - Window.FileSelector(write_ui, 'Export Wavefront OBJ', sys.makename(ext='.obj')) +class EXPORT_OT_obj(bpy.types.Operator): + ''' + Currently the exporter lacks these features: + * nurbs + * multiple scene export (only active scene is written) + * particles + ''' + __idname__ = "export.obj" + __label__ = 'Export OBJ' + + # 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 exporting the OBJ file", maxlen= 1024, default= ""), + + # context group + bpy.props.BoolProperty(attr="use_selection", name="Selection Only", description="", default= False), + bpy.props.BoolProperty(attr="use_all_scenes", name="All Scenes", description="", default= False), + bpy.props.BoolProperty(attr="use_animation", name="All Animation", description="", default= False), + + # object group + bpy.props.BoolProperty(attr="use_modifiers", name="Apply Modifiers", description="", default= True), + bpy.props.BoolProperty(attr="use_rotate90", name="Rotate X90", description="", default= True), + + # extra data group + bpy.props.BoolProperty(attr="use_edges", name="Edges", description="", default= True), + bpy.props.BoolProperty(attr="use_normals", name="Normals", description="", default= False), + bpy.props.BoolProperty(attr="use_hq_normals", name="High Quality Normals", description="", default= True), + bpy.props.BoolProperty(attr="use_uvs", name="UVs", description="", default= True), + bpy.props.BoolProperty(attr="use_materials", name="Materials", description="", default= True), + bpy.props.BoolProperty(attr="copy_images", name="Copy Images", description="", default= False), + bpy.props.BoolProperty(attr="use_triangles", name="Triangulate", description="", default= False), + bpy.props.BoolProperty(attr="use_vertex_groups", name="Polygroups", description="", default= False), + bpy.props.BoolProperty(attr="use_nurbs", name="Nurbs", description="", default= False), + + # grouping group + bpy.props.BoolProperty(attr="use_blen_objects", name="Objects as OBJ Objects", description="", default= True), + bpy.props.BoolProperty(attr="group_by_object", name="Objects as OBJ Groups ", description="", default= False), + bpy.props.BoolProperty(attr="group_by_material", name="Material Groups", description="", default= False), + bpy.props.BoolProperty(attr="keep_vertex_order", name="Keep Vertex Order", description="", default= False) + ] + + def execute(self, context): + + do_export(self.path, context, + EXPORT_TRI=self.use_triangles, + EXPORT_EDGES=self.use_edges, + EXPORT_NORMALS=self.use_normals, + EXPORT_NORMALS_HQ=self.use_hq_normals, + EXPORT_UV=self.use_uvs, + EXPORT_MTL=self.use_materials, + EXPORT_COPY_IMAGES=self.copy_images, + EXPORT_APPLY_MODIFIERS=self.use_modifiers, + EXPORT_ROTX90=self.use_rotate90, + EXPORT_BLEN_OBS=self.use_blen_objects, + EXPORT_GROUP_BY_OB=self.group_by_object, + EXPORT_GROUP_BY_MAT=self.group_by_material, + EXPORT_KEEP_VERT_ORDER=self.keep_vertex_order, + EXPORT_POLYGROUPS=self.use_vertex_groups, + EXPORT_CURVE_AS_NURBS=self.use_nurbs, + EXPORT_SEL_ONLY=self.use_selection, + EXPORT_ALL_SCENES=self.use_all_scenes) + + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + def poll(self, context): # Poll isnt working yet + print("Poll") + return context.active_object != None + +bpy.ops.add(EXPORT_OT_obj) + +if __name__ == "__main__": + bpy.ops.EXPORT_OT_obj(filename="/tmp/test.obj") + +# CONVERSION ISSUES +# - matrix problem +# - duplis - only tested dupliverts +# - NURBS - needs API additions +# - all scenes export +# + normals calculation +# - get rid of cleanName somehow diff --git a/release/scripts/io/export_ply.py b/release/scripts/io/export_ply.py index 46d08050302..8e79c3741bb 100644 --- a/release/scripts/io/export_ply.py +++ b/release/scripts/io/export_ply.py @@ -1,16 +1,4 @@ -#!BPY - -""" -Name: 'Stanford PLY (*.ply)...' -Blender: 241 -Group: 'Export' -Tooltip: 'Export active object to Stanford PLY format' -""" - import bpy -import Blender -from Blender import Mesh, Scene, Window, sys, Image, Draw -import BPyMesh __author__ = "Bruce Merry" __version__ = "0.93" @@ -62,84 +50,105 @@ Only one mesh can be exported at a time. def rvec3d(v): return round(v[0], 6), round(v[1], 6), round(v[2], 6) def rvec2d(v): return round(v[0], 6), round(v[1], 6) -def file_callback(filename): +def write(filename, scene, ob, \ + EXPORT_APPLY_MODIFIERS= True,\ + EXPORT_NORMALS= True,\ + EXPORT_UV= True,\ + EXPORT_COLORS= True\ + ): if not filename.lower().endswith('.ply'): filename += '.ply' - scn= bpy.data.scenes.active - ob= scn.objects.active if not ob: - Blender.Draw.PupMenu('Error%t|Select 1 active object') + raise Exception("Error, Select 1 active object") return - file = open(filename, 'wb') + file = open(filename, 'w') - EXPORT_APPLY_MODIFIERS = Draw.Create(1) - EXPORT_NORMALS = Draw.Create(1) - EXPORT_UV = Draw.Create(1) - EXPORT_COLORS = Draw.Create(1) - #EXPORT_EDGES = Draw.Create(0) - - pup_block = [\ - ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data.'),\ - ('Normals', EXPORT_NORMALS, 'Export vertex normal data.'),\ - ('UVs', EXPORT_UV, 'Export texface UV coords.'),\ - ('Colors', EXPORT_COLORS, 'Export vertex Colors.'),\ - #('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\ - ] - - if not Draw.PupBlock('Export...', pup_block): - return + #EXPORT_EDGES = Draw.Create(0) + """ is_editmode = Blender.Window.EditMode() if is_editmode: Blender.Window.EditMode(0, '', 0) Window.WaitCursor(1) + """ - EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val - EXPORT_NORMALS = EXPORT_NORMALS.val - EXPORT_UV = EXPORT_UV.val - EXPORT_COLORS = EXPORT_COLORS.val - #EXPORT_EDGES = EXPORT_EDGES.val - - mesh = BPyMesh.getMeshFromObject(ob, None, EXPORT_APPLY_MODIFIERS, False, scn) + #mesh = BPyMesh.getMeshFromObject(ob, None, EXPORT_APPLY_MODIFIERS, False, scn) # XXX + if EXPORT_APPLY_MODIFIERS: + mesh = ob.create_mesh(True, 'PREVIEW') + else: + mesh = ob.data if not mesh: - Blender.Draw.PupMenu('Error%t|Could not get mesh data from active object') + raise ("Error, could not get mesh data from active object") return - mesh.transform(ob.matrixWorld) + # mesh.transform(ob.matrixWorld) # XXX - faceUV = mesh.faceUV - vertexUV = mesh.vertexUV - vertexColors = mesh.vertexColors + faceUV = len(mesh.uv_textures) > 0 + vertexUV = len(mesh.sticky) > 0 + vertexColors = len(mesh.vertex_colors) > 0 - if (not faceUV) and (not vertexUV): EXPORT_UV = False + if (not faceUV) and (not vertexUV): EXPORT_UV = False if not vertexColors: EXPORT_COLORS = False if not EXPORT_UV: faceUV = vertexUV = False if not EXPORT_COLORS: vertexColors = False + + if faceUV: + active_uv_layer = None + for lay in mesh.uv_textures: + if lay.active: + active_uv_layer= lay.data + break + if not active_uv_layer: + EXPORT_UV = False + faceUV = None + + if vertexColors: + active_col_layer = None + for lay in mesh.vertex_colors: + if lay.active: + active_col_layer= lay.data + if not active_col_layer: + EXPORT_COLORS = False + vertexColors = None # incase color = uvcoord = uvcoord_key = normal = normal_key = None - verts = [] # list of dictionaries + mesh_verts = mesh.verts # save a lookup + ply_verts = [] # list of dictionaries # vdict = {} # (index, normal, uv) -> new index - vdict = [{} for i in xrange(len(mesh.verts))] + vdict = [{} for i in range(len(mesh_verts))] + ply_faces = [[] for f in range(len(mesh.faces))] vert_count = 0 for i, f in enumerate(mesh.faces): + + smooth = f.smooth if not smooth: - normal = tuple(f.no) + normal = tuple(f.normal) normal_key = rvec3d(normal) + + if faceUV: + uv = active_uv_layer[i] + uv = uv.uv1, uv.uv2, uv.uv3, uv.uv4 # XXX - crufty :/ + if vertexColors: + col = active_col_layer[i] + col = col.color1, col.color2, col.color3, col.color4 + + f_verts= f.verts + + pf= ply_faces[i] + for j, vidx in enumerate(f_verts): + v = mesh_verts[vidx] - if faceUV: uv = f.uv - if vertexColors: col = f.col - for j, v in enumerate(f): if smooth: - normal= tuple(v.no) + normal= tuple(v.normal) normal_key = rvec3d(normal) if faceUV: @@ -149,33 +158,41 @@ def file_callback(filename): uvcoord= v.uvco[0], 1.0-v.uvco[1] uvcoord_key = rvec2d(uvcoord) - if vertexColors: color= col[j].r, col[j].g, col[j].b + if vertexColors: + color= col[j] + color= int(color[0]*255.0), int(color[1]*255.0), int(color[2]*255.0) key = normal_key, uvcoord_key, color - vdict_local = vdict[v.index] + vdict_local = vdict[vidx] + pf_vidx = vdict_local.get(key) # Will be None initially - if (not vdict_local) or (not vdict_local.has_key(key)): - vdict_local[key] = vert_count; - verts.append( (tuple(v.co), normal, uvcoord, color) ) + if pf_vidx == None: # same as vdict_local.has_key(key) + pf_vidx = vdict_local[key] = vert_count; + ply_verts.append((vidx, normal, uvcoord, color)) vert_count += 1 - + + pf.append(pf_vidx) file.write('ply\n') file.write('format ascii 1.0\n') - file.write('comment Created by Blender3D %s - www.blender.org, source file: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] )) + version = "2.5" # Blender.Get('version') + file.write('comment Created by Blender3D %s - www.blender.org, source file: %s\n' % (version, bpy.data.filename.split('/')[-1].split('\\')[-1] )) - file.write('element vertex %d\n' % len(verts)) + file.write('element vertex %d\n' % len(ply_verts)) file.write('property float x\n') file.write('property float y\n') file.write('property float z\n') + + # XXX + """ if EXPORT_NORMALS: file.write('property float nx\n') file.write('property float ny\n') file.write('property float nz\n') - + """ if EXPORT_UV: file.write('property float s\n') file.write('property float t\n') @@ -188,41 +205,75 @@ def file_callback(filename): file.write('property list uchar uint vertex_indices\n') file.write('end_header\n') - for i, v in enumerate(verts): - file.write('%.6f %.6f %.6f ' % v[0]) # co + for i, v in enumerate(ply_verts): + file.write('%.6f %.6f %.6f ' % tuple(mesh_verts[v[0]].co)) # co + """ if EXPORT_NORMALS: file.write('%.6f %.6f %.6f ' % v[1]) # no - - if EXPORT_UV: - file.write('%.6f %.6f ' % v[2]) # uv - if EXPORT_COLORS: - file.write('%u %u %u' % v[3]) # col + """ + if EXPORT_UV: file.write('%.6f %.6f ' % v[2]) # uv + if EXPORT_COLORS: file.write('%u %u %u' % v[3]) # col file.write('\n') - for (i, f) in enumerate(mesh.faces): - file.write('%d ' % len(f)) - smooth = f.smooth - if not smooth: no = rvec3d(f.no) - - if faceUV: uv = f.uv - if vertexColors: col = f.col - for j, v in enumerate(f): - if f.smooth: normal= rvec3d(v.no) - else: normal= no - if faceUV: uvcoord= rvec2d((uv[j][0], 1.0-uv[j][1])) - elif vertexUV: uvcoord= rvec2d((v.uvco[0], 1.0-v.uvco[1])) - if vertexColors: color= col[j].r, col[j].g, col[j].b - - file.write('%d ' % vdict[v.index][normal, uvcoord, color]) - - file.write('\n') + for pf in ply_faces: + if len(pf)==3: file.write('3 %d %d %d\n' % tuple(pf)) + else: file.write('4 %d %d %d %d\n' % tuple(pf)) + file.close() + print("writing", filename, "done") + + if EXPORT_APPLY_MODIFIERS: + bpy.data.remove_mesh(mesh) + # XXX + """ if is_editmode: Blender.Window.EditMode(1, '', 0) + """ + +class EXPORT_OT_ply(bpy.types.Operator): + '''Export a single object as a stanford PLY with normals, colours and texture coordinates.''' + __idname__ = "export.ply" + __label__ = "Export PLY" + + # 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 exporting the PLY file", maxlen= 1024, default= ""), + bpy.props.BoolProperty(attr="use_modifiers", name="Apply Modifiers", description="Apply Modifiers to the exported mesh", default= True), + bpy.props.BoolProperty(attr="use_normals", name="Export Normals", description="Export Normals for smooth and hard shaded faces", default= True), + bpy.props.BoolProperty(attr="use_uvs", name="Export UVs", description="Exort the active UV layer", default= True), + bpy.props.BoolProperty(attr="use_colors", name="Export Vertex Colors", description="Exort the active vertex color layer", default= True) + ] + + def poll(self, context): + return context.active_object != None + + def execute(self, context): + # print("Selected: " + context.active_object.name) + + if not self.path: + raise Exception("filename not set") + + write(self.path, context.scene, context.active_object,\ + EXPORT_APPLY_MODIFIERS = self.use_modifiers, + EXPORT_NORMALS = self.use_normals, + EXPORT_UV = self.use_uvs, + EXPORT_COLORS = self.use_colors, + ) + + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + +bpy.ops.add(EXPORT_OT_ply) + +if __name__ == "__main__": + bpy.ops.EXPORT_OT_ply(path="/tmp/test.ply") -def main(): - Blender.Window.FileSelector(file_callback, 'PLY Export', Blender.sys.makename(ext='.ply')) -if __name__=='__main__': - main() diff --git a/release/scripts/io/export_x3d.py b/release/scripts/io/export_x3d.py index b12ff67d8a6..db29afc7d6d 100644 --- a/release/scripts/io/export_x3d.py +++ b/release/scripts/io/export_x3d.py @@ -53,22 +53,30 @@ Known issues:<br> # Library dependancies #################################### -import Blender -from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh -from Blender.Scene import Render import math -import BPyObject -import BPyMesh +import os + +import bpy +import Mathutils + +from export_3ds import create_derived_objects, free_derived_objects + +# import Blender +# from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh +# from Blender.Scene import Render +# import BPyObject +# import BPyMesh # DEG2RAD=0.017453292519943295 -MATWORLD= Blender.Mathutils.RotationMatrix(-90, 4, 'x') +MATWORLD= Mathutils.RotationMatrix(-90, 4, 'x') #################################### # Global Variables #################################### -filename = Blender.Get('filename') +filename = "" +# filename = Blender.Get('filename') _safeOverwrite = True extension = '' @@ -109,7 +117,7 @@ class x3d_class: import gzip self.file = gzip.open(filename, "w") except: - print "failed to import compression modules, exporting uncompressed" + print("failed to import compression modules, exporting uncompressed") self.filename = filename[:-1] # remove trailing z if self.file == None: @@ -161,8 +169,10 @@ class x3d_class: self.file.write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.0//EN\" \"http://www.web3d.org/specifications/x3d-3.0.dtd\">\n") self.file.write("<X3D version=\"3.0\" profile=\"Immersive\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema-instance\" xsd:noNamespaceSchemaLocation=\"http://www.web3d.org/specifications/x3d-3.0.xsd\">\n") self.file.write("<head>\n") - self.file.write("\t<meta name=\"filename\" content=\"%s\" />\n" % sys.basename(bfile)) - self.file.write("\t<meta name=\"generator\" content=\"Blender %s\" />\n" % Blender.Get('version')) + self.file.write("\t<meta name=\"filename\" content=\"%s\" />\n" % os.path.basename(bfile)) + # self.file.write("\t<meta name=\"filename\" content=\"%s\" />\n" % sys.basename(bfile)) + self.file.write("\t<meta name=\"generator\" content=\"Blender %s\" />\n" % '2.5') + # self.file.write("\t<meta name=\"generator\" content=\"Blender %s\" />\n" % Blender.Get('version')) self.file.write("\t<meta name=\"translator\" content=\"X3D exporter v1.55 (2006/01/17)\" />\n") self.file.write("</head>\n") self.file.write("<Scene>\n") @@ -206,9 +216,12 @@ class x3d_class: ''' def writeViewpoint(self, ob, mat, scene): - context = scene.render - ratio = float(context.imageSizeY())/float(context.imageSizeX()) - lens = (360* (math.atan(ratio *16 / ob.data.getLens()) / math.pi))*(math.pi/180) + context = scene.render_data + # context = scene.render + ratio = float(context.resolution_x)/float(context.resolution_y) + # ratio = float(context.imageSizeY())/float(context.imageSizeX()) + lens = (360* (math.atan(ratio *16 / ob.data.lens) / math.pi))*(math.pi/180) + # lens = (360* (math.atan(ratio *16 / ob.data.getLens()) / math.pi))*(math.pi/180) lens = min(lens, math.pi) # get the camera location, subtract 90 degress from X to orient like X3D does @@ -216,7 +229,8 @@ class x3d_class: loc = self.rotatePointForVRML(mat.translationPart()) rot = mat.toEuler() - rot = (((rot[0]-90)*DEG2RAD), rot[1]*DEG2RAD, rot[2]*DEG2RAD) + rot = (((rot[0]-90)), rot[1], rot[2]) + # rot = (((rot[0]-90)*DEG2RAD), rot[1]*DEG2RAD, rot[2]*DEG2RAD) nRot = self.rotatePointForVRML( rot ) # convert to Quaternion and to Angle Axis Q = self.eulerToQuaternions(nRot[0], nRot[1], nRot[2]) @@ -232,13 +246,18 @@ class x3d_class: def writeFog(self, world): if world: - mtype = world.getMistype() - mparam = world.getMist() - grd = world.getHor() + mtype = world.mist.falloff + # mtype = world.getMistype() + mparam = world.mist + # mparam = world.getMist() + grd = world.horizon_color + # grd = world.getHor() grd0, grd1, grd2 = grd[0], grd[1], grd[2] else: return - if (mtype == 1 or mtype == 2): + if (mtype == 'LINEAR' or mtype == 'INVERSE_QUADRATIC'): + mtype = 1 if mtype == 'LINEAR' else 2 + # if (mtype == 1 or mtype == 2): self.file.write("<Fog fogType=\"%s\" " % self.namesFog[mtype]) self.file.write("color=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("visibilityRange=\"%s\" />\n\n" % round(mparam[2],self.cp)) @@ -251,7 +270,8 @@ class x3d_class: def writeSpotLight(self, ob, mtx, lamp, world): safeName = self.cleanStr(ob.name) if world: - ambi = world.amb + ambi = world.ambient_color + # ambi = world.amb ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 else: ambi = 0 @@ -259,7 +279,8 @@ class x3d_class: # compute cutoff and beamwidth intensity=min(lamp.energy/1.75,1.0) - beamWidth=((lamp.spotSize*math.pi)/180.0)*.37; + beamWidth=((lamp.spot_size*math.pi)/180.0)*.37; + # beamWidth=((lamp.spotSize*math.pi)/180.0)*.37; cutOffAngle=beamWidth*1.3 dx,dy,dz=self.computeDirection(mtx) @@ -270,12 +291,14 @@ class x3d_class: #location=(ob.matrixWorld*MATWORLD).translationPart() # now passed location=(mtx*MATWORLD).translationPart() - radius = lamp.dist*math.cos(beamWidth) + radius = lamp.distance*math.cos(beamWidth) + # radius = lamp.dist*math.cos(beamWidth) self.file.write("<SpotLight DEF=\"%s\" " % safeName) self.file.write("radius=\"%s\" " % (round(radius,self.cp))) self.file.write("ambientIntensity=\"%s\" " % (round(ambientIntensity,self.cp))) self.file.write("intensity=\"%s\" " % (round(intensity,self.cp))) - self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp))) + self.file.write("color=\"%s %s %s\" " % (round(lamp.color[0],self.cp), round(lamp.color[1],self.cp), round(lamp.color[2],self.cp))) + # self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp))) self.file.write("beamWidth=\"%s\" " % (round(beamWidth,self.cp))) self.file.write("cutOffAngle=\"%s\" " % (round(cutOffAngle,self.cp))) self.file.write("direction=\"%s %s %s\" " % (round(dx,3),round(dy,3),round(dz,3))) @@ -285,7 +308,8 @@ class x3d_class: def writeDirectionalLight(self, ob, mtx, lamp, world): safeName = self.cleanStr(ob.name) if world: - ambi = world.amb + ambi = world.ambient_color + # ambi = world.amb ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 else: ambi = 0 @@ -295,14 +319,16 @@ class x3d_class: (dx,dy,dz)=self.computeDirection(mtx) self.file.write("<DirectionalLight DEF=\"%s\" " % safeName) self.file.write("ambientIntensity=\"%s\" " % (round(ambientIntensity,self.cp))) - self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp))) + self.file.write("color=\"%s %s %s\" " % (round(lamp.color[0],self.cp), round(lamp.color[1],self.cp), round(lamp.color[2],self.cp))) + # self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp))) self.file.write("intensity=\"%s\" " % (round(intensity,self.cp))) self.file.write("direction=\"%s %s %s\" />\n\n" % (round(dx,4),round(dy,4),round(dz,4))) def writePointLight(self, ob, mtx, lamp, world): safeName = self.cleanStr(ob.name) if world: - ambi = world.amb + ambi = world.ambient_color + # ambi = world.amb ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 else: ambi = 0 @@ -313,9 +339,11 @@ class x3d_class: self.file.write("<PointLight DEF=\"%s\" " % safeName) self.file.write("ambientIntensity=\"%s\" " % (round(ambientIntensity,self.cp))) - self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp))) + self.file.write("color=\"%s %s %s\" " % (round(lamp.color[0],self.cp), round(lamp.color[1],self.cp), round(lamp.color[2],self.cp))) + # self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp))) self.file.write("intensity=\"%s\" " % (round( min(lamp.energy/1.75,1.0) ,self.cp))) - self.file.write("radius=\"%s\" " % lamp.dist ) + self.file.write("radius=\"%s\" " % lamp.distance ) + # self.file.write("radius=\"%s\" " % lamp.dist ) self.file.write("location=\"%s %s %s\" />\n\n" % (round(location[0],3), round(location[1],3), round(location[2],3))) ''' def writeNode(self, ob, mtx): @@ -357,24 +385,41 @@ class x3d_class: vColors={} # 'multi':1 meshName = self.cleanStr(ob.name) - meshME = self.cleanStr(ob.getData(mesh=1).name) # We dont care if its the mesh name or not + meshME = self.cleanStr(ob.data.name) # We dont care if its the mesh name or not + # meshME = self.cleanStr(ob.getData(mesh=1).name) # We dont care if its the mesh name or not if len(mesh.faces) == 0: return - mode = 0 - if mesh.faceUV: - for face in mesh.faces: - mode |= face.mode + mode = [] + # mode = 0 + if mesh.active_uv_texture: + # if mesh.faceUV: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: + if face.halo and 'HALO' not in mode: + mode += ['HALO'] + if face.billboard and 'BILLBOARD' not in mode: + mode += ['BILLBOARD'] + if face.object_color and 'OBJECT_COLOR' not in mode: + mode += ['OBJECT_COLOR'] + if face.collision and 'COLLISION' not in mode: + mode += ['COLLISION'] + # mode |= face.mode - if mode & Mesh.FaceModes.HALO and self.halonode == 0: + if 'HALO' in mode and self.halonode == 0: + # if mode & Mesh.FaceModes.HALO and self.halonode == 0: self.writeIndented("<Billboard axisOfRotation=\"0 0 0\">\n",1) self.halonode = 1 - elif mode & Mesh.FaceModes.BILLBOARD and self.billnode == 0: + elif 'BILLBOARD' in mode and self.billnode == 0: + # elif mode & Mesh.FaceModes.BILLBOARD and self.billnode == 0: self.writeIndented("<Billboard axisOfRotation=\"0 1 0\">\n",1) self.billnode = 1 - elif mode & Mesh.FaceModes.OBCOL and self.matonly == 0: + elif 'OBJECT_COLOR' in mode and self.matonly == 0: + # elif mode & Mesh.FaceModes.OBCOL and self.matonly == 0: self.matonly = 1 - elif mode & Mesh.FaceModes.TILES and self.tilenode == 0: - self.tilenode = 1 - elif not mode & Mesh.FaceModes.DYNAMIC and self.collnode == 0: + # TF_TILES is marked as deprecated in DNA_meshdata_types.h + # elif mode & Mesh.FaceModes.TILES and self.tilenode == 0: + # self.tilenode = 1 + elif 'COLLISION' not in mode and self.collnode == 0: + # elif not mode & Mesh.FaceModes.DYNAMIC and self.collnode == 0: self.writeIndented("<Collision enabled=\"false\">\n",1) self.collnode = 1 @@ -383,7 +428,7 @@ class x3d_class: if nIFSCnt > 1: self.writeIndented("<Group DEF=\"%s%s\">\n" % ("G_", meshName),1) - if sided.has_key('two') and sided['two'] > 0: + if 'two' in sided and sided['two'] > 0: bTwoSided=1 else: bTwoSided=0 @@ -396,34 +441,44 @@ class x3d_class: quat = mtx.toQuat() rot= quat.axis - # self.writeIndented('<Transform rotation="%.6f %.6f %.6f %.6f">\n' % (rot[0], rot[1], rot[2], rot[3])) self.writeIndented('<Transform DEF="%s" translation="%.6f %.6f %.6f" scale="%.6f %.6f %.6f" rotation="%.6f %.6f %.6f %.6f">\n' % \ - (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle*DEG2RAD) ) + (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle) ) + # self.writeIndented('<Transform DEF="%s" translation="%.6f %.6f %.6f" scale="%.6f %.6f %.6f" rotation="%.6f %.6f %.6f %.6f">\n' % \ + # (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle*DEG2RAD) ) self.writeIndented("<Shape>\n",1) maters=mesh.materials hasImageTexture=0 issmooth=0 - if len(maters) > 0 or mesh.faceUV: + if len(maters) > 0 or mesh.active_uv_texture: + # if len(maters) > 0 or mesh.faceUV: self.writeIndented("<Appearance>\n", 1) # right now this script can only handle a single material per mesh. if len(maters) >= 1: mat=maters[0] - matFlags = mat.getMode() - if not matFlags & Blender.Material.Modes['TEXFACE']: - self.writeMaterial(mat, self.cleanStr(maters[0].name,''), world) + # matFlags = mat.getMode() + if not mat.face_texture: + # if not matFlags & Blender.Material.Modes['TEXFACE']: + self.writeMaterial(mat, self.cleanStr(mat.name,''), world) + # self.writeMaterial(mat, self.cleanStr(maters[0].name,''), world) if len(maters) > 1: - print "Warning: mesh named %s has multiple materials" % meshName - print "Warning: only one material per object handled" + print("Warning: mesh named %s has multiple materials" % meshName) + print("Warning: only one material per object handled") #-- textures - if mesh.faceUV: - for face in mesh.faces: - if (hasImageTexture == 0) and (face.image): + face = None + if mesh.active_uv_texture: + # if mesh.faceUV: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: + if face.image: + # if (hasImageTexture == 0) and (face.image): self.writeImageTexture(face.image) - hasImageTexture=1 # keep track of face texture - if self.tilenode == 1: + # hasImageTexture=1 # keep track of face texture + break + if self.tilenode == 1 and face and face.image: + # if self.tilenode == 1: self.writeIndented("<TextureTransform scale=\"%s %s\" />\n" % (face.image.xrep, face.image.yrep)) self.tilenode = 0 self.writeIndented("</Appearance>\n", -1) @@ -433,7 +488,7 @@ class x3d_class: # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5 ifStyle="IndexedFaceSet" # look up mesh name, use it if available - if self.meshNames.has_key(meshME): + if meshME in self.meshNames: self.writeIndented("<%s USE=\"ME_%s\">" % (ifStyle, meshME), 1) self.meshNames[meshME]+=1 else: @@ -453,11 +508,13 @@ class x3d_class: issmooth=1 break if issmooth==1: - creaseAngle=(mesh.degr)*(math.pi/180.0) + creaseAngle=(mesh.autosmooth_angle)*(math.pi/180.0) + # creaseAngle=(mesh.degr)*(math.pi/180.0) self.file.write("creaseAngle=\"%s\" " % (round(creaseAngle,self.cp))) #--- output textureCoordinates if UV texture used - if mesh.faceUV: + if mesh.active_uv_texture: + # if mesh.faceUV: if self.matonly == 1 and self.share == 1: self.writeFaceColors(mesh) elif hasImageTexture == 1: @@ -471,7 +528,8 @@ class x3d_class: self.writeCoordinates(ob, mesh, meshName, EXPORT_TRI) #--- output textureCoordinates if UV texture used - if mesh.faceUV: + if mesh.active_uv_texture: + # if mesh.faceUV: if hasImageTexture == 1: self.writeTextureCoordinates(mesh) elif self.matonly == 1 and self.share == 1: @@ -511,16 +569,22 @@ class x3d_class: if self.writingcoords == 0: self.file.write('coordIndex="') for face in mesh.faces: - fv = face.v + fv = face.verts + # fv = face.v - if len(face)==3: - self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) + if len(fv)==3: + # if len(face)==3: + self.file.write("%i %i %i -1, " % (fv[0], fv[1], fv[2])) + # self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) else: if EXPORT_TRI: - self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) - self.file.write("%i %i %i -1, " % (fv[0].index, fv[2].index, fv[3].index)) + self.file.write("%i %i %i -1, " % (fv[0], fv[1], fv[2])) + # self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index)) + self.file.write("%i %i %i -1, " % (fv[0], fv[2], fv[3])) + # self.file.write("%i %i %i -1, " % (fv[0].index, fv[2].index, fv[3].index)) else: - self.file.write("%i %i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index, fv[3].index)) + self.file.write("%i %i %i %i -1, " % (fv[0], fv[1], fv[2], fv[3])) + # self.file.write("%i %i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index, fv[3].index)) self.file.write("\">\n") else: @@ -538,8 +602,13 @@ class x3d_class: texIndexList=[] j=0 - for face in mesh.faces: - for uv in face.uv: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: + uvs = face.uv + # uvs = [face.uv1, face.uv2, face.uv3, face.uv4] if face.verts[3] else [face.uv1, face.uv2, face.uv3] + + for uv in uvs: + # for uv in face.uv: texIndexList.append(j) texCoordList.append(uv) j=j+1 @@ -547,7 +616,7 @@ class x3d_class: if self.writingtexture == 0: self.file.write("\n\t\t\ttexCoordIndex=\"") texIndxStr="" - for i in xrange(len(texIndexList)): + for i in range(len(texIndexList)): texIndxStr = texIndxStr + "%d, " % texIndexList[i] if texIndexList[i]==-1: self.file.write(texIndxStr) @@ -555,7 +624,7 @@ class x3d_class: self.file.write("\"\n\t\t\t") else: self.writeIndented("<TextureCoordinate point=\"", 1) - for i in xrange(len(texCoordList)): + for i in range(len(texCoordList)): self.file.write("%s %s, " % (round(texCoordList[i][0],self.tp), round(texCoordList[i][1],self.tp))) self.file.write("\" />") self.writeIndented("\n", -1) @@ -563,43 +632,61 @@ class x3d_class: def writeFaceColors(self, mesh): if self.writingcolor == 0: self.file.write("colorPerVertex=\"false\" ") - else: + elif mesh.active_vertex_color: + # else: self.writeIndented("<Color color=\"", 1) - for face in mesh.faces: - if face.col: - c=face.col[0] - if self.verbose > 2: - print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b) - aColor = self.rgbToFS(c) - self.file.write("%s, " % aColor) + for face in mesh.active_vertex_color.data: + c = face.color1 + if self.verbose > 2: + print("Debug: face.col r=%d g=%d b=%d" % (c[0], c[1], c[2])) + # print("Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)) + aColor = self.rgbToFS(c) + self.file.write("%s, " % aColor) + + # for face in mesh.faces: + # if face.col: + # c=face.col[0] + # if self.verbose > 2: + # print("Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)) + # aColor = self.rgbToFS(c) + # self.file.write("%s, " % aColor) self.file.write("\" />") self.writeIndented("\n",-1) def writeMaterial(self, mat, matName, world): # look up material name, use it if available - if self.matNames.has_key(matName): + if matName in self.matNames: self.writeIndented("<Material USE=\"MA_%s\" />\n" % matName) self.matNames[matName]+=1 return; self.matNames[matName]=1 - ambient = mat.amb/3 - diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2] + ambient = mat.ambient/3 + # ambient = mat.amb/3 + diffuseR, diffuseG, diffuseB = tuple(mat.diffuse_color) + # diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2] if world: - ambi = world.getAmb() - ambi0, ambi1, ambi2 = (ambi[0]*mat.amb)*2, (ambi[1]*mat.amb)*2, (ambi[2]*mat.amb)*2 + ambi = world.ambient_color + # ambi = world.getAmb() + ambi0, ambi1, ambi2 = (ambi[0]*mat.ambient)*2, (ambi[1]*mat.ambient)*2, (ambi[2]*mat.ambient)*2 + # ambi0, ambi1, ambi2 = (ambi[0]*mat.amb)*2, (ambi[1]*mat.amb)*2, (ambi[2]*mat.amb)*2 else: ambi0, ambi1, ambi2 = 0, 0, 0 emisR, emisG, emisB = (diffuseR*mat.emit+ambi0)/2, (diffuseG*mat.emit+ambi1)/2, (diffuseB*mat.emit+ambi2)/2 - shininess = mat.hard/512.0 - specR = (mat.specCol[0]+0.001)/(1.25/(mat.spec+0.001)) - specG = (mat.specCol[1]+0.001)/(1.25/(mat.spec+0.001)) - specB = (mat.specCol[2]+0.001)/(1.25/(mat.spec+0.001)) + shininess = mat.specular_hardness/512.0 + # shininess = mat.hard/512.0 + specR = (mat.specular_color[0]+0.001)/(1.25/(mat.specular_intensity+0.001)) + # specR = (mat.specCol[0]+0.001)/(1.25/(mat.spec+0.001)) + specG = (mat.specular_color[1]+0.001)/(1.25/(mat.specular_intensity+0.001)) + # specG = (mat.specCol[1]+0.001)/(1.25/(mat.spec+0.001)) + specB = (mat.specular_color[2]+0.001)/(1.25/(mat.specular_intensity+0.001)) + # specB = (mat.specCol[2]+0.001)/(1.25/(mat.spec+0.001)) transp = 1-mat.alpha - matFlags = mat.getMode() - if matFlags & Blender.Material.Modes['SHADELESS']: + # matFlags = mat.getMode() + if mat.shadeless: + # if matFlags & Blender.Material.Modes['SHADELESS']: ambient = 1 shine = 1 specR = emitR = diffuseR @@ -617,7 +704,7 @@ class x3d_class: def writeImageTexture(self, image): name = image.name filename = image.filename.split('/')[-1].split('\\')[-1] - if self.texNames.has_key(name): + if name in self.texNames: self.writeIndented("<ImageTexture USE=\"%s\" />\n" % self.cleanStr(name)) self.texNames[name] += 1 return @@ -630,10 +717,13 @@ class x3d_class: def writeBackground(self, world, alltextures): if world: worldname = world.name else: return - blending = world.getSkytype() - grd = world.getHor() + blending = (world.blend_sky, world.paper_sky, world.real_sky) + # blending = world.getSkytype() + grd = world.horizon_color + # grd = world.getHor() grd0, grd1, grd2 = grd[0], grd[1], grd[2] - sky = world.getZen() + sky = world.zenith_color + # sky = world.getZen() sky0, sky1, sky2 = sky[0], sky[1], sky[2] mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2] mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2 @@ -641,27 +731,32 @@ class x3d_class: if worldname not in self.namesStandard: self.file.write("DEF=\"%s\" " % self.secureName(worldname)) # No Skytype - just Hor color - if blending == 0: + if blending == (0, 0, 0): + # if blending == 0: self.file.write("groundColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("skyColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) # Blend Gradient - elif blending == 1: + elif blending == (1, 0, 0): + # elif blending == 1: self.file.write("groundColor=\"%s %s %s, " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) self.file.write("skyColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.file.write("%s %s %s\" skyAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) # Blend+Real Gradient Inverse - elif blending == 3: + elif blending == (1, 0, 1): + # elif blending == 3: self.file.write("groundColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.file.write("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) self.file.write("skyColor=\"%s %s %s, " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("%s %s %s\" skyAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp))) # Paper - just Zen Color - elif blending == 4: + elif blending == (0, 0, 1): + # elif blending == 4: self.file.write("groundColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.file.write("skyColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) # Blend+Real+Paper - komplex gradient - elif blending == 7: + elif blending == (1, 1, 1): + # elif blending == 7: self.writeIndented("groundColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) self.writeIndented("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.writeIndented("skyColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) @@ -670,22 +765,43 @@ class x3d_class: else: self.file.write("groundColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp))) self.file.write("skyColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp))) + alltexture = len(alltextures) - for i in xrange(alltexture): - namemat = alltextures[i].name - pic = alltextures[i].getImage() + + for i in range(alltexture): + tex = alltextures[i] + + if tex.type != 'IMAGE' or tex.image == None: + continue + + namemat = tex.name + # namemat = alltextures[i].name + + pic = tex.image + + # using .expandpath just in case, os.path may not expect // + basename = os.path.basename(pic.get_abs_filename()) + + pic = alltextures[i].image + # pic = alltextures[i].getImage() if (namemat == "back") and (pic != None): - self.file.write("\n\tbackUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.file.write("\n\tbackUrl=\"%s\" " % basename) + # self.file.write("\n\tbackUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "bottom") and (pic != None): - self.writeIndented("bottomUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("bottomUrl=\"%s\" " % basename) + # self.writeIndented("bottomUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "front") and (pic != None): - self.writeIndented("frontUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("frontUrl=\"%s\" " % basename) + # self.writeIndented("frontUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "left") and (pic != None): - self.writeIndented("leftUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("leftUrl=\"%s\" " % basename) + # self.writeIndented("leftUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "right") and (pic != None): - self.writeIndented("rightUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("rightUrl=\"%s\" " % basename) + # self.writeIndented("rightUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) elif (namemat == "top") and (pic != None): - self.writeIndented("topUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + self.writeIndented("topUrl=\"%s\" " % basename) + # self.writeIndented("topUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) self.writeIndented("/>\n\n") ########################################################## @@ -697,7 +813,7 @@ class x3d_class: EXPORT_TRI= False,\ ): - print "Info: starting X3D export to " + self.filename + "..." + print("Info: starting X3D export to " + self.filename + "...") self.writeHeader() # self.writeScript() self.writeNavigationInfo(scene) @@ -706,44 +822,65 @@ class x3d_class: self.proto = 0 - # COPIED FROM OBJ EXPORTER - if EXPORT_APPLY_MODIFIERS: - temp_mesh_name = '~tmp-mesh' + # # COPIED FROM OBJ EXPORTER + # if EXPORT_APPLY_MODIFIERS: + # temp_mesh_name = '~tmp-mesh' - # Get the container mesh. - used for applying modifiers and non mesh objects. - containerMesh = meshName = tempMesh = None - for meshName in Blender.NMesh.GetNames(): - if meshName.startswith(temp_mesh_name): - tempMesh = Mesh.Get(meshName) - if not tempMesh.users: - containerMesh = tempMesh - if not containerMesh: - containerMesh = Mesh.New(temp_mesh_name) + # # Get the container mesh. - used for applying modifiers and non mesh objects. + # containerMesh = meshName = tempMesh = None + # for meshName in Blender.NMesh.GetNames(): + # if meshName.startswith(temp_mesh_name): + # tempMesh = Mesh.Get(meshName) + # if not tempMesh.users: + # containerMesh = tempMesh + # if not containerMesh: + # containerMesh = Mesh.New(temp_mesh_name) # -------------------------- - for ob_main in scene.objects.context: - for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): + for ob_main in [o for o in scene.objects if o.is_visible()]: + # for ob_main in scene.objects.context: + + free, derived = create_derived_objects(ob_main) + + if derived == None: continue + + for ob, ob_mat in derived: + # for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): objType=ob.type objName=ob.name self.matonly = 0 - if objType == "Camera": + if objType == "CAMERA": + # if objType == "Camera": self.writeViewpoint(ob, ob_mat, scene) - elif objType in ("Mesh", "Curve", "Surf", "Text") : - if EXPORT_APPLY_MODIFIERS or objType != 'Mesh': - me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scene) + elif objType in ("MESH", "CURVE", "SURF", "TEXT") : + # elif objType in ("Mesh", "Curve", "Surf", "Text") : + if EXPORT_APPLY_MODIFIERS or objType != 'MESH': + # if EXPORT_APPLY_MODIFIERS or objType != 'Mesh': + me = ob.create_mesh(EXPORT_APPLY_MODIFIERS, 'PREVIEW') + # me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scene) else: - me = ob.getData(mesh=1) + me = ob.data + # me = ob.getData(mesh=1) self.writeIndexedFaceSet(ob, me, ob_mat, world, EXPORT_TRI = EXPORT_TRI) - elif objType == "Lamp": + + # free mesh created with create_mesh() + if me != ob.data: + bpy.data.remove_mesh(me) + + elif objType == "LAMP": + # elif objType == "Lamp": data= ob.data datatype=data.type - if datatype == Lamp.Types.Lamp: + if datatype == 'POINT': + # if datatype == Lamp.Types.Lamp: self.writePointLight(ob, ob_mat, data, world) - elif datatype == Lamp.Types.Spot: + elif datatype == 'SPOT': + # elif datatype == Lamp.Types.Spot: self.writeSpotLight(ob, ob_mat, data, world) - elif datatype == Lamp.Types.Sun: + elif datatype == 'SUN': + # elif datatype == Lamp.Types.Sun: self.writeDirectionalLight(ob, ob_mat, data, world) else: self.writeDirectionalLight(ob, ob_mat, data, world) @@ -753,12 +890,15 @@ class x3d_class: else: #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType) pass - + + if free: + free_derived_objects(ob_main) + self.file.write("\n</Scene>\n</X3D>") - if EXPORT_APPLY_MODIFIERS: - if containerMesh: - containerMesh.verts = None + # if EXPORT_APPLY_MODIFIERS: + # if containerMesh: + # containerMesh.verts = None self.cleanup() @@ -771,7 +911,7 @@ class x3d_class: self.texNames={} self.matNames={} self.indentLevel=0 - print "Info: finished X3D export to %s\n" % self.filename + print("Info: finished X3D export to %s\n" % self.filename) def cleanStr(self, name, prefix='rsvd_'): """cleanStr(name,prefix) - try to create a valid VRML DEF name from object name""" @@ -807,15 +947,18 @@ class x3d_class: faceMap={} nFaceIndx=0 - if mesh.faceUV: - for face in mesh.faces: + if mesh.active_uv_texture: + # if mesh.faceUV: + for face in mesh.active_uv_texture.data: + # for face in mesh.faces: sidename=''; - if face.mode & Mesh.FaceModes.TWOSIDE: + if face.twoside: + # if face.mode & Mesh.FaceModes.TWOSIDE: sidename='two' else: sidename='one' - if sided.has_key(sidename): + if sidename in sided: sided[sidename]+=1 else: sided[sidename]=1 @@ -829,56 +972,63 @@ class x3d_class: imageMap[faceName]=[face.image.name,sidename,face] if self.verbose > 2: - for faceName in imageMap.iterkeys(): + for faceName in imageMap.keys(): ifs=imageMap[faceName] - print "Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \ - (faceName, ifs[0], ifs[1], len(ifs)-2) + print("Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \ + (faceName, ifs[0], ifs[1], len(ifs)-2)) return len(imageMap) def faceToString(self,face): - print "Debug: face.flag=0x%x (bitflags)" % face.flag + print("Debug: face.flag=0x%x (bitflags)" % face.flag) if face.sel: - print "Debug: face.sel=true" + print("Debug: face.sel=true") - print "Debug: face.mode=0x%x (bitflags)" % face.mode + print("Debug: face.mode=0x%x (bitflags)" % face.mode) if face.mode & Mesh.FaceModes.TWOSIDE: - print "Debug: face.mode twosided" + print("Debug: face.mode twosided") - print "Debug: face.transp=0x%x (enum)" % face.transp + print("Debug: face.transp=0x%x (enum)" % face.transp) if face.transp == Mesh.FaceTranspModes.SOLID: - print "Debug: face.transp.SOLID" + print("Debug: face.transp.SOLID") if face.image: - print "Debug: face.image=%s" % face.image.name - print "Debug: face.materialIndex=%d" % face.materialIndex - - def getVertexColorByIndx(self, mesh, indx): - c = None - for face in mesh.faces: - j=0 - for vertex in face.v: - if vertex.index == indx: - c=face.col[j] - break - j=j+1 - if c: break - return c + print("Debug: face.image=%s" % face.image.name) + print("Debug: face.materialIndex=%d" % face.materialIndex) + + # XXX not used + # def getVertexColorByIndx(self, mesh, indx): + # c = None + # for face in mesh.faces: + # j=0 + # for vertex in face.v: + # if vertex.index == indx: + # c=face.col[j] + # break + # j=j+1 + # if c: break + # return c def meshToString(self,mesh): - print "Debug: mesh.hasVertexUV=%d" % mesh.vertexColors - print "Debug: mesh.faceUV=%d" % mesh.faceUV - print "Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours() - print "Debug: mesh.verts=%d" % len(mesh.verts) - print "Debug: mesh.faces=%d" % len(mesh.faces) - print "Debug: mesh.materials=%d" % len(mesh.materials) + # print("Debug: mesh.hasVertexUV=%d" % mesh.vertexColors) + print("Debug: mesh.faceUV=%d" % (len(mesh.uv_textures) > 0)) + # print("Debug: mesh.faceUV=%d" % mesh.faceUV) + print("Debug: mesh.hasVertexColours=%d" % (len(mesh.vertex_colors) > 0)) + # print("Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours()) + print("Debug: mesh.verts=%d" % len(mesh.verts)) + print("Debug: mesh.faces=%d" % len(mesh.faces)) + print("Debug: mesh.materials=%d" % len(mesh.materials)) def rgbToFS(self, c): - s="%s %s %s" % ( - round(c.r/255.0,self.cp), - round(c.g/255.0,self.cp), - round(c.b/255.0,self.cp)) + s="%s %s %s" % (round(c[0]/255.0,self.cp), + round(c[1]/255.0,self.cp), + round(c[2]/255.0,self.cp)) + + # s="%s %s %s" % ( + # round(c.r/255.0,self.cp), + # round(c.g/255.0,self.cp), + # round(c.b/255.0,self.cp)) return s def computeDirection(self, mtx): @@ -886,9 +1036,10 @@ class x3d_class: ax,ay,az = (mtx*MATWORLD).toEuler() - ax *= DEG2RAD - ay *= DEG2RAD - az *= DEG2RAD + # ax *= DEG2RAD + # ay *= DEG2RAD + # az *= DEG2RAD + # rot X x1=x y1=y*math.cos(ax)-z*math.sin(ax) @@ -924,7 +1075,7 @@ class x3d_class: self.indentLevel = self.indentLevel + inc spaces="" - for x in xrange(self.indentLevel): + for x in range(self.indentLevel): spaces = spaces + "\t" self.file.write(spaces + s) @@ -975,11 +1126,11 @@ class x3d_class: # Callbacks, needed before Main ########################################################## -def x3d_export(filename, \ - EXPORT_APPLY_MODIFIERS= False,\ - EXPORT_TRI= False,\ - EXPORT_GZIP= False,\ - ): +def x3d_export(filename, + context, + EXPORT_APPLY_MODIFIERS=False, + EXPORT_TRI=False, + EXPORT_GZIP=False): if EXPORT_GZIP: if not filename.lower().endswith('.x3dz'): @@ -989,9 +1140,13 @@ def x3d_export(filename, \ filename = '.'.join(filename.split('.')[:-1]) + '.x3d' - scene = Blender.Scene.GetCurrent() + scene = context.scene + # scene = Blender.Scene.GetCurrent() world = scene.world - alltextures = Blender.Texture.Get() + + # XXX these are global textures while .Get() returned only scene's? + alltextures = bpy.data.textures + # alltextures = Blender.Texture.Get() wrlexport=x3d_class(filename) wrlexport.export(\ @@ -1045,7 +1200,41 @@ def x3d_export_ui(filename): ######################################################### -if __name__ == '__main__': - Blender.Window.FileSelector(x3d_export_ui,"Export X3D", Blender.Get('filename').replace('.blend', '.x3d')) +# if __name__ == '__main__': +# Blender.Window.FileSelector(x3d_export_ui,"Export X3D", Blender.Get('filename').replace('.blend', '.x3d')) + +class EXPORT_OT_x3d(bpy.types.Operator): + ''' + X3D Exporter + ''' + __idname__ = "export.x3d" + __label__ = 'Export X3D' + + # 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 exporting the X3D file", maxlen= 1024, default= ""), + + bpy.props.BoolProperty(attr="apply_modifiers", name="Apply Modifiers", description="Use transformed mesh data from each object.", default=True), + bpy.props.BoolProperty(attr="triangulate", name="Triangulate", description="Triangulate quads.", default=False), + bpy.props.BoolProperty(attr="compress", name="Compress", description="GZip the resulting file, requires a full python install.", default=False), + ] + + def execute(self, context): + x3d_export(self.path, context, self.apply_modifiers, self.triangulate, self.compress) + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + def poll(self, context): # Poll isnt working yet + print("Poll") + return context.active_object != None +bpy.ops.add(EXPORT_OT_x3d) +# NOTES +# - blender version is hardcoded diff --git a/release/scripts/io/import_3ds.py b/release/scripts/io/import_3ds.py index bcde82c4869..339fac839ea 100644 --- a/release/scripts/io/import_3ds.py +++ b/release/scripts/io/import_3ds.py @@ -125,28 +125,36 @@ Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen). # Importing modules -import Blender +import os +import time +import struct + +from import_obj import unpack_face_list, load_image + import bpy -from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils -from Blender.Mathutils import Vector -import BPyImage +import Mathutils + +# import Blender +# from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils +# from Blender.Mathutils import Vector +# import BPyImage -import BPyMessages +# import BPyMessages -try: - from struct import calcsize, unpack -except: - calcsize= unpack= None +# 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 +# # 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= [] +BOUNDS_3DS = [] #this script imports uvcoords as sticky vertex coords @@ -154,9 +162,9 @@ BOUNDS_3DS= [] #which shold be more useful. def createBlenderTexture(material, name, image): - texture= bpy.data.textures.new(name) + texture = bpy.data.textures.new(name) texture.setType('Image') - texture.image= image + texture.image = image material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) @@ -167,189 +175,201 @@ def createBlenderTexture(material, name, image): #Some of the chunks that we will see #----- Primary Chunk, at the beginning of each file -PRIMARY= long('0x4D4D',16) +PRIMARY = int('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 -EDITKEYFRAME= long('0xB000',16); #This is the header for all of the key frame info +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... +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 = long('0xA000',16) # This holds the material name -MAT_AMBIENT = long('0xA010',16) # Ambient color of the object/material -MAT_DIFFUSE = long('0xA020',16) # This holds the color of the object/material -MAT_SPECULAR = long('0xA030',16) # SPecular color of the object/material -MAT_SHINESS = long('0xA040',16) # ?? -MAT_TRANSPARENCY= long('0xA050',16) # Transparency value of material -MAT_SELF_ILLUM = long('0xA080',16) # Self Illumination value of material -MAT_WIRE = long('0xA085',16) # Only render's wireframe - -MAT_TEXTURE_MAP = long('0xA200',16) # This is a header for a new texture map -MAT_SPECULAR_MAP= long('0xA204',16) # This is a header for a new specular map -MAT_OPACITY_MAP = long('0xA210',16) # This is a header for a new opacity map -MAT_REFLECTION_MAP= long('0xA220',16) # This is a header for a new reflection map -MAT_BUMP_MAP = long('0xA230',16) # This is a header for a new bump map -MAT_MAP_FILENAME = long('0xA300',16) # This holds the file name of the texture - -MAT_FLOAT_COLOR = long ('0x0010', 16) #color defined as 3 floats -MAT_24BIT_COLOR = long ('0x0011', 16) #color defined as 3 bytes +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 = long('0x4100',16); # This lets us know that we are reading a new object -OBJECT_LAMP = long('0x4600',16); # This lets un know we are reading a light object -OBJECT_LAMP_SPOT = long('0x4610',16); # The light is a spotloght. -OBJECT_LAMP_OFF = long('0x4620',16); # The light off. -OBJECT_LAMP_ATTENUATE = long('0x4625',16); -OBJECT_LAMP_RAYSHADE = long('0x4627',16); -OBJECT_LAMP_SHADOWED = long('0x4630',16); -OBJECT_LAMP_LOCAL_SHADOW = long('0x4640',16); -OBJECT_LAMP_LOCAL_SHADOW2 = long('0x4641',16); -OBJECT_LAMP_SEE_CONE = long('0x4650',16); -OBJECT_LAMP_SPOT_RECTANGULAR= long('0x4651',16); -OBJECT_LAMP_SPOT_OVERSHOOT= long('0x4652',16); -OBJECT_LAMP_SPOT_PROJECTOR= long('0x4653',16); -OBJECT_LAMP_EXCLUDE= long('0x4654',16); -OBJECT_LAMP_RANGE= long('0x4655',16); -OBJECT_LAMP_ROLL= long('0x4656',16); -OBJECT_LAMP_SPOT_ASPECT= long('0x4657',16); -OBJECT_LAMP_RAY_BIAS= long('0x4658',16); -OBJECT_LAMP_INNER_RANGE= long('0x4659',16); -OBJECT_LAMP_OUTER_RANGE= long('0x465A',16); -OBJECT_LAMP_MULTIPLIER = long('0x465B',16); -OBJECT_LAMP_AMBIENT_LIGHT = long('0x4680',16); - - - -OBJECT_CAMERA= long('0x4700',16); # This lets un know we are reading a camera 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= long('0x4720',16); # The camera range values +OBJECT_CAM_RANGES= int('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 +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 +scn = None #the chunk class class chunk: - ID=0 - length=0 - bytes_read=0 + ID = 0 + length = 0 + bytes_read = 0 #we don't read in the bytes_read, we compute that binary_format='<HI' def __init__(self): - self.ID=0 - self.length=0 - self.bytes_read=0 + self.ID = 0 + self.length = 0 + self.bytes_read = 0 def dump(self): - print 'ID: ', self.ID - print 'ID in hex: ', hex(self.ID) - print 'length: ', self.length - print 'bytes_read: ', self.bytes_read + print('ID: ', self.ID) + print('ID in hex: ', hex(self.ID)) + print('length: ', self.length) + print('bytes_read: ', self.bytes_read) def read_chunk(file, chunk): - temp_data=file.read(calcsize(chunk.binary_format)) - data=unpack(chunk.binary_format, temp_data) - chunk.ID=data[0] - chunk.length=data[1] + temp_data = file.read(struct.calcsize(chunk.binary_format)) + data = struct.unpack(chunk.binary_format, temp_data) + chunk.ID = data[0] + chunk.length = data[1] #update the bytes read function - chunk.bytes_read=6 + chunk.bytes_read = 6 #if debugging #chunk.dump() def read_string(file): #read in the characters till we get a null character - s='' - while not s.endswith('\x00'): - s+=unpack( '<c', file.read(1) )[0] + s = b'' +# s = '' + while not s.endswith(b'\x00'): +# while not s.endswith('\x00'): + s += struct.unpack('<c', file.read(1))[0] +# s += struct.unpack( '<c', file.read(1) )[0] #print 'string: ',s - + + s = str(s[:-1], 'ASCII') +# print("read string", s) + #remove the null character from the string - return s[:-1] + return s +# return s[:-1] ###################################################### # IMPORT ###################################################### def process_next_object_chunk(file, previous_chunk): - new_chunk=chunk() - temp_chunk=chunk() + new_chunk = chunk() + temp_chunk = chunk() - while (previous_chunk.bytes_read<previous_chunk.length): + while (previous_chunk.bytes_read < previous_chunk.length): #read the next chunk read_chunk(file, new_chunk) def skip_to_end(file, skip_chunk): - buffer_size=skip_chunk.length-skip_chunk.bytes_read + buffer_size = skip_chunk.length - skip_chunk.bytes_read binary_format='%ic' % buffer_size - temp_data=file.read(calcsize(binary_format)) - skip_chunk.bytes_read+=buffer_size + temp_data = file.read(struct.calcsize(binary_format)) + skip_chunk.bytes_read += buffer_size def add_texture_to_material(image, texture, material, mapto): - if mapto=='DIFFUSE': - map=Texture.MapTo.COL - elif mapto=='SPECULAR': - map=Texture.MapTo.SPEC - elif mapto=='OPACITY': - map=Texture.MapTo.ALPHA - elif mapto=='BUMP': - map=Texture.MapTo.NOR - else: - print '/tError: Cannot map to "%s"\n\tassuming diffuse color. modify material "%s" later.' % (mapto, material.name) - map=Texture.MapTo.COL - - if image: texture.setImage(image) # double check its an image. - free_tex_slots= [i for i, tex in enumerate( material.getTextures() ) if tex==None] - if not free_tex_slots: - print '/tError: Cannot add "%s" map. 10 Texture slots alredy used.' % mapto - else: - material.setTexture(free_tex_slots[0],texture,Texture.TexCo.UV,map) +# if mapto=='DIFFUSE': +# map = Texture.MapTo.COL +# elif mapto=='SPECULAR': +# map = Texture.MapTo.SPEC +# elif mapto=='OPACITY': +# map = Texture.MapTo.ALPHA +# elif mapto=='BUMP': +# map = Texture.MapTo.NOR +# else: + if mapto not in ("COLOR", "SPECULARITY", "ALPHA", "NORMAL"): + print('/tError: Cannot map to "%s"\n\tassuming diffuse color. modify material "%s" later.' % (mapto, material.name)) + mapto = "COLOR" +# map = Texture.MapTo.COL + + if image: texture.image = image +# if image: texture.setImage(image) # double check its an image. + + material.add_texture(texture, "UV", mapto) +# free_tex_slots = [i for i, tex in enumerate( material.getTextures() ) if tex == None] +# if not free_tex_slots: +# print('/tError: Cannot add "%s" map. 10 Texture slots alredy used.' % mapto) +# else: +# material.setTexture(free_tex_slots[0],texture,Texture.TexCo.UV,map) def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): #print previous_chunk.bytes_read, 'BYTES READ' - contextObName= None - contextLamp= [None, None] # object, Data - contextMaterial= None - contextMatrix_rot= None # Blender.Mathutils.Matrix(); contextMatrix.identity() - #contextMatrix_tx= None # Blender.Mathutils.Matrix(); contextMatrix.identity() - contextMesh_vertls= None - contextMesh_facels= None - contextMeshMaterials= {} # matname:[face_idxs] - contextMeshUV= None - - TEXTURE_DICT={} - MATDICT={} - TEXMODE= Mesh.FaceModes['TEX'] + contextObName = None + contextLamp = [None, None] # object, Data + contextMaterial = None + contextMatrix_rot = None # Blender.Mathutils.Matrix(); contextMatrix.identity() + #contextMatrix_tx = None # Blender.Mathutils.Matrix(); contextMatrix.identity() + contextMesh_vertls = None + contextMesh_facels = None + contextMeshMaterials = {} # matname:[face_idxs] + contextMeshUV = None + + TEXTURE_DICT = {} + MATDICT = {} +# TEXMODE = Mesh.FaceModes['TEX'] # Localspace variable names, faster. - STRUCT_SIZE_1CHAR= calcsize('c') - STRUCT_SIZE_2FLOAT= calcsize('2f') - STRUCT_SIZE_3FLOAT= calcsize('3f') - STRUCT_SIZE_UNSIGNED_SHORT= calcsize('H') - STRUCT_SIZE_4UNSIGNED_SHORT= calcsize('4H') - STRUCT_SIZE_4x3MAT= calcsize('ffffffffffff') - _STRUCT_SIZE_4x3MAT= calcsize('fffffffffffff') - # STRUCT_SIZE_4x3MAT= calcsize('ffffffffffff') + STRUCT_SIZE_1CHAR = struct.calcsize('c') + STRUCT_SIZE_2FLOAT = struct.calcsize('2f') + STRUCT_SIZE_3FLOAT = struct.calcsize('3f') + STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize('H') + STRUCT_SIZE_4UNSIGNED_SHORT = struct.calcsize('4H') + STRUCT_SIZE_4x3MAT = struct.calcsize('ffffffffffff') + _STRUCT_SIZE_4x3MAT = struct.calcsize('fffffffffffff') + # STRUCT_SIZE_4x3MAT = calcsize('ffffffffffff') # print STRUCT_SIZE_4x3MAT, ' STRUCT_SIZE_4x3MAT' def putContextMesh(myContextMesh_vertls, myContextMesh_facels, myContextMeshMaterials): - materialFaces= set() # faces that have a material. Can optimize? + materialFaces = set() # faces that have a material. Can optimize? # Now make copies with assigned materils. @@ -360,13 +380,13 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): ''' faceVertUsers = [False] * len(myContextMesh_vertls) - ok=0 + ok = 0 for fIdx in faces: for vindex in myContextMesh_facels[fIdx]: faceVertUsers[vindex] = True if matName != None: # if matName is none then this is a set(), meaning we are using the untextured faces and do not need to store textured faces. materialFaces.add(fIdx) - ok=1 + ok = 1 if not ok: return @@ -374,374 +394,465 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): myVertMapping = {} vertMappingIndex = 0 - vertsToUse = [i for i in xrange(len(myContextMesh_vertls)) if faceVertUsers[i]] + vertsToUse = [i for i in range(len(myContextMesh_vertls)) if faceVertUsers[i]] myVertMapping = dict( [ (ii, i) for i, ii in enumerate(vertsToUse) ] ) tempName= '%s_%s' % (contextObName, matName) # matName may be None. - bmesh = bpy.data.meshes.new(tempName) + bmesh = bpy.data.add_mesh(tempName) +# bmesh = bpy.data.meshes.new(tempName) if matName == None: - img= None + img = None else: bmat = MATDICT[matName][1] - bmesh.materials= [bmat] - try: img= TEXTURE_DICT[bmat.name] - except: img= None + bmesh.add_material(bmat) +# bmesh.materials = [bmat] + try: img = TEXTURE_DICT[bmat.name] + except: img = None - bmesh_verts = bmesh.verts - bmesh_verts.extend( [Vector()] ) - bmesh_verts.extend( [myContextMesh_vertls[i] for i in vertsToUse] ) - # +1 because of DUMMYVERT - face_mapping= bmesh.faces.extend( [ [ bmesh_verts[ myVertMapping[vindex]+1] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces ], indexList=True ) - - if bmesh.faces and (contextMeshUV or img): - bmesh.faceUV= 1 - for ii, i in enumerate(faces): - - # Mapped index- faces may have not been added- if so, then map to the correct index - # BUGGY API - face_mapping is not always the right length - map_index= face_mapping[ii] - - if map_index != None: - targetFace= bmesh.faces[map_index] - if contextMeshUV: - # v.index-1 because of the DUMMYVERT - targetFace.uv= [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]] - if img: - targetFace.image= img +# bmesh_verts = bmesh.verts + if len(vertsToUse): + bmesh.add_geometry(len(vertsToUse), 0, len(faces)) + + # XXX why add extra vertex? +# bmesh_verts.extend( [Vector()] ) + bmesh.verts.foreach_set("co", [x for tup in [myContextMesh_vertls[i] for i in vertsToUse] for x in tup]) +# bmesh_verts.extend( [myContextMesh_vertls[i] for i in vertsToUse] ) + + # +1 because of DUMMYVERT + bmesh.faces.foreach_set("verts_raw", unpack_face_list([[myVertMapping[vindex] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces])) +# face_mapping = bmesh.faces.extend( [ [ bmesh_verts[ myVertMapping[vindex]+1] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces ], indexList=True ) + + if bmesh.faces and (contextMeshUV or img): + bmesh.add_uv_texture() +# bmesh.faceUV = 1 + for ii, i in enumerate(faces): + + # Mapped index- faces may have not been added- if so, then map to the correct index + # BUGGY API - face_mapping is not always the right length +# map_index = face_mapping[ii] + + if 1: +# if map_index != None: + targetFace = bmesh.faces[ii] +# targetFace = bmesh.faces[map_index] + + uf = bmesh.active_uv_texture.data[ii] + + if contextMeshUV: + # v.index-1 because of the DUMMYVERT + uvs = [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]] + + if len(myContextMesh_facels[i]) == 3: + uf.uv1, uf.uv2, uf.uv3, uf.uv4 = uvs + [(0.0, 0.0)] + else: + uf.uv1, uf.uv2, uf.uv3, uf.uv4 = uvs +# targetFace.uv = [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]] + if img: + uf.image = img +# targetFace.image = img # bmesh.transform(contextMatrix) - ob = SCN_OBJECTS.new(bmesh, tempName) + ob = bpy.data.add_object("MESH", tempName) + ob.data = bmesh + SCN.add_object(ob) +# ob = SCN_OBJECTS.new(bmesh, tempName) ''' if contextMatrix_tx: ob.setMatrix(contextMatrix_tx) ''' if contextMatrix_rot: - ob.setMatrix(contextMatrix_rot) + # ob.matrix = [x for row in contextMatrix_rot for x in row] + ob.matrix = contextMatrix_rot +# ob.setMatrix(contextMatrix_rot) importedObjects.append(ob) - bmesh.calcNormals() + bmesh.update() +# bmesh.calcNormals() - for matName, faces in myContextMeshMaterials.iteritems(): + for matName, faces in myContextMeshMaterials.items(): makeMeshMaterialCopy(matName, faces) - if len(materialFaces)!=len(myContextMesh_facels): + if len(materialFaces) != len(myContextMesh_facels): # Invert material faces. makeMeshMaterialCopy(None, set(range(len( myContextMesh_facels ))) - materialFaces) #raise 'Some UnMaterialed faces', len(contextMesh.faces) #a spare chunk - new_chunk= chunk() - temp_chunk= chunk() + new_chunk = chunk() + temp_chunk = chunk() CreateBlenderObject = False + def read_float_color(temp_chunk): + temp_data = file.read(struct.calcsize('3f')) + temp_chunk.bytes_read += 12 + return [float(col) for col in struct.unpack('<3f', temp_data)] + + def read_byte_color(temp_chunk): + temp_data = file.read(struct.calcsize('3B')) + temp_chunk.bytes_read += 3 + return [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb + + def read_texture(new_chunk, temp_chunk, name, mapto): + new_texture = bpy.data.add_texture('Diffuse') + new_texture.type = 'IMAGE' + + img = None + while (new_chunk.bytes_read < new_chunk.length): + #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length + read_chunk(file, temp_chunk) + + if (temp_chunk.ID == MAT_MAP_FILENAME): + texture_name = read_string(file) + img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname) + new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed + + else: + skip_to_end(file, temp_chunk) + + new_chunk.bytes_read += temp_chunk.bytes_read + + # add the map to the material in the right channel + if img: + add_texture_to_material(img, new_texture, contextMaterial, mapto) + + dirname = os.path.dirname(FILENAME) + #loop through all the data for this chunk (previous chunk) and see what it is - while (previous_chunk.bytes_read<previous_chunk.length): + while (previous_chunk.bytes_read < previous_chunk.length): #print '\t', previous_chunk.bytes_read, 'keep going' #read the next chunk #print 'reading a chunk' read_chunk(file, new_chunk) #is it a Version chunk? - if (new_chunk.ID==VERSION): - #print 'if (new_chunk.ID==VERSION):' + if (new_chunk.ID == VERSION): + #print 'if (new_chunk.ID == VERSION):' #print 'found a VERSION chunk' #read in the version of the file #it's an unsigned short (H) - temp_data= file.read(calcsize('I')) - version = unpack('<I', temp_data)[0] - new_chunk.bytes_read+= 4 #read the 4 bytes for the version number + temp_data = file.read(struct.calcsize('I')) + version = struct.unpack('<I', temp_data)[0] + new_chunk.bytes_read += 4 #read the 4 bytes for the version number #this loader works with version 3 and below, but may not with 4 and above - if (version>3): - print '\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version + if (version > 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):' + 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 + new_chunk.bytes_read += temp_chunk.bytes_read #is it an object chunk? - elif (new_chunk.ID==OBJECT): + elif (new_chunk.ID == OBJECT): if CreateBlenderObject: putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) - contextMesh_vertls= []; contextMesh_facels= [] + contextMesh_vertls = []; contextMesh_facels = [] ## preparando para receber o proximo objeto - contextMeshMaterials= {} # matname:[face_idxs] - contextMeshUV= None - #contextMesh.vertexUV= 1 # Make sticky coords. + contextMeshMaterials = {} # matname:[face_idxs] + contextMeshUV = None + #contextMesh.vertexUV = 1 # Make sticky coords. # Reset matrix - contextMatrix_rot= None - #contextMatrix_tx= None + contextMatrix_rot = None + #contextMatrix_tx = None - CreateBlenderObject= True - tempName= read_string(file) - contextObName= tempName + 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 'elif (new_chunk.ID==MATERIAL):' - contextMaterial= bpy.data.materials.new('Material') + 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) + 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 + new_chunk.bytes_read += len(material_name)+1 - contextMaterial.name= material_name.rstrip() # remove trailing whitespace + 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):' + 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): - temp_data=file.read(calcsize('3f')) - temp_chunk.bytes_read+=12 - contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)] - elif (temp_chunk.ID==MAT_24BIT_COLOR): - temp_data=file.read(calcsize('3B')) - temp_chunk.bytes_read+= 3 - contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + 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 + new_chunk.bytes_read += temp_chunk.bytes_read - elif (new_chunk.ID==MAT_DIFFUSE): - #print 'elif (new_chunk.ID==MAT_DIFFUSE):' + 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): - temp_data=file.read(calcsize('3f')) - temp_chunk.bytes_read+=12 - contextMaterial.rgbCol=[float(col) for col in unpack('<3f', temp_data)] - elif (temp_chunk.ID==MAT_24BIT_COLOR): - temp_data=file.read(calcsize('3B')) - temp_chunk.bytes_read+= 3 - contextMaterial.rgbCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + 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) - new_chunk.bytes_read+= temp_chunk.bytes_read - elif (new_chunk.ID==MAT_SPECULAR): - #print 'elif (new_chunk.ID==MAT_SPECULAR):' +# 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): - temp_data=file.read(calcsize('3f')) - temp_chunk.bytes_read+=12 - contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)] - elif (temp_chunk.ID==MAT_24BIT_COLOR): - temp_data=file.read(calcsize('3B')) - temp_chunk.bytes_read+= 3 - contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb + 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): - #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<new_chunk.length): - #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length - read_chunk(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<new_chunk.length): +# #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length +# read_chunk(file, temp_chunk) - if (temp_chunk.ID==MAT_MAP_FILENAME): - texture_name=read_string(file) - #img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) - img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) - new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed +# if (temp_chunk.ID==MAT_MAP_FILENAME): +# texture_name=read_string(file) +# #img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) +# img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) +# new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed - else: - skip_to_end(file, temp_chunk) +# else: +# skip_to_end(file, temp_chunk) - new_chunk.bytes_read+= temp_chunk.bytes_read - - #add the map to the material in the right channel - if img: - add_texture_to_material(img, new_texture, contextMaterial, 'DIFFUSE') - - elif (new_chunk.ID==MAT_SPECULAR_MAP): - #print 'elif (new_chunk.ID==MAT_SPECULAR_MAP):' - new_texture= bpy.data.textures.new('Specular') - new_texture.setType('Image') - img = None - while (new_chunk.bytes_read<new_chunk.length): - read_chunk(file, temp_chunk) +# new_chunk.bytes_read+= temp_chunk.bytes_read + +# #add the map to the material in the right channel +# if img: +# add_texture_to_material(img, new_texture, contextMaterial, 'DIFFUSE') + + elif (new_chunk.ID == MAT_SPECULAR_MAP): + read_texture(new_chunk, temp_chunk, "Specular", "SPECULARITY") +# #print 'elif (new_chunk.ID == MAT_SPECULAR_MAP):' +# new_texture = bpy.data.textures.new('Specular') +# new_texture.setType('Image') +# img = None +# while (new_chunk.bytes_read < new_chunk.length): +# read_chunk(file, temp_chunk) - if (temp_chunk.ID==MAT_MAP_FILENAME): - texture_name= read_string(file) - #img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) - img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) - new_chunk.bytes_read+= (len(texture_name)+1) #plus one for the null character that gets removed - else: - skip_to_end(file, temp_chunk) +# if (temp_chunk.ID == MAT_MAP_FILENAME): +# texture_name = read_string(file) +# #img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME) +# img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH) +# new_chunk.bytes_read+= (len(texture_name)+1) #plus one for the null character that gets removed +# else: +# skip_to_end(file, temp_chunk) - new_chunk.bytes_read+= temp_chunk.bytes_read +# new_chunk.bytes_read += temp_chunk.bytes_read - #add the map to the material in the right channel - if img: - add_texture_to_material(img, new_texture, contextMaterial, 'SPECULAR') - - elif (new_chunk.ID==MAT_OPACITY_MAP): - #print 'new_texture=Blender.Texture.New('Opacity')' - new_texture= bpy.data.textures.new('Opacity') - new_texture.setType('Image') - img = None - while (new_chunk.bytes_read<new_chunk.length): - read_chunk(file, temp_chunk) +# #add the map to the material in the right channel +# if img: +# add_texture_to_material(img, new_texture, contextMaterial, 'SPECULAR') + + elif (new_chunk.ID == MAT_OPACITY_MAP): + read_texture(new_chunk, temp_chunk, "Opacity", "ALPHA") +# #print 'new_texture = Blender.Texture.New('Opacity')' +# new_texture = bpy.data.textures.new('Opacity') +# new_texture.setType('Image') +# img = None +# while (new_chunk.bytes_read < new_chunk.length): +# read_chunk(file, temp_chunk) - if (temp_chunk.ID==MAT_MAP_FILENAME): - texture_name= read_string(file) - #img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) - img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) - new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed - else: - skip_to_end(file, temp_chunk) +# if (temp_chunk.ID == MAT_MAP_FILENAME): +# texture_name = read_string(file) +# #img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME) +# img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH) +# new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed +# else: +# skip_to_end(file, temp_chunk) - new_chunk.bytes_read+= temp_chunk.bytes_read - #add the map to the material in the right channel - if img: - add_texture_to_material(img, new_texture, contextMaterial, 'OPACITY') - - elif (new_chunk.ID==MAT_BUMP_MAP): - #print 'elif (new_chunk.ID==MAT_BUMP_MAP):' - new_texture= bpy.data.textures.new('Bump') - new_texture.setType('Image') - img = None - while (new_chunk.bytes_read<new_chunk.length): - read_chunk(file, temp_chunk) +# new_chunk.bytes_read += temp_chunk.bytes_read +# #add the map to the material in the right channel +# if img: +# add_texture_to_material(img, new_texture, contextMaterial, 'OPACITY') + + elif (new_chunk.ID == MAT_BUMP_MAP): + read_texture(new_chunk, temp_chunk, "Bump", "NORMAL") +# #print 'elif (new_chunk.ID == MAT_BUMP_MAP):' +# new_texture = bpy.data.textures.new('Bump') +# new_texture.setType('Image') +# img = None +# while (new_chunk.bytes_read < new_chunk.length): +# read_chunk(file, temp_chunk) - if (temp_chunk.ID==MAT_MAP_FILENAME): - texture_name= read_string(file) - #img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) - img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) - new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed - else: - skip_to_end(file, temp_chunk) +# if (temp_chunk.ID == MAT_MAP_FILENAME): +# texture_name = read_string(file) +# #img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME) +# img = BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH) +# new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed +# else: +# skip_to_end(file, temp_chunk) - new_chunk.bytes_read+=temp_chunk.bytes_read +# new_chunk.bytes_read += temp_chunk.bytes_read - #add the map to the material in the right channel - if img: - add_texture_to_material(img, new_texture, contextMaterial, 'BUMP') +# #add the map to the material in the right channel +# if img: +# add_texture_to_material(img, new_texture, contextMaterial, 'BUMP') - elif (new_chunk.ID==MAT_TRANSPARENCY): - #print 'elif (new_chunk.ID==MAT_TRANSPARENCY):' + 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_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) - temp_chunk.bytes_read+=2 - contextMaterial.alpha= 1-(float(unpack('<H', temp_data)[0])/100) - new_chunk.bytes_read+=temp_chunk.bytes_read + temp_chunk.bytes_read += 2 + contextMaterial.alpha = 1-(float(struct.unpack('<H', temp_data)[0])/100) + new_chunk.bytes_read += temp_chunk.bytes_read - elif (new_chunk.ID==OBJECT_LAMP): # Basic lamp support. + elif (new_chunk.ID == OBJECT_LAMP): # Basic lamp support. - temp_data=file.read(STRUCT_SIZE_3FLOAT) + temp_data = file.read(STRUCT_SIZE_3FLOAT) - x,y,z=unpack('<3f', temp_data) - new_chunk.bytes_read+=STRUCT_SIZE_3FLOAT + x,y,z = struct.unpack('<3f', temp_data) + new_chunk.bytes_read += STRUCT_SIZE_3FLOAT + + ob = bpy.data.add_object("LAMP", "Lamp") + ob.data = bpy.data.add_lamp("Lamp") + SCN.add_object(ob) - contextLamp[1]= bpy.data.lamps.new() - contextLamp[0]= SCN_OBJECTS.new(contextLamp[1]) + contextLamp[1]= ob.data +# contextLamp[1]= bpy.data.lamps.new() + contextLamp[0]= ob +# contextLamp[0]= SCN_OBJECTS.new(contextLamp[1]) importedObjects.append(contextLamp[0]) #print 'number of faces: ', num_faces #print x,y,z - contextLamp[0].setLocation(x,y,z) + contextLamp[0].location = (x, y, z) +# contextLamp[0].setLocation(x,y,z) # Reset matrix - contextMatrix_rot= None - #contextMatrix_tx= None + contextMatrix_rot = None + #contextMatrix_tx = None #print contextLamp.name, - elif (new_chunk.ID==OBJECT_MESH): + elif (new_chunk.ID == OBJECT_MESH): # print 'Found an OBJECT_MESH chunk' pass - elif (new_chunk.ID==OBJECT_VERTICES): + elif (new_chunk.ID == OBJECT_VERTICES): ''' Worldspace vertex locations ''' - # print 'elif (new_chunk.ID==OBJECT_VERTICES):' - temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) - num_verts=unpack('<H', temp_data)[0] - new_chunk.bytes_read+=2 + # print 'elif (new_chunk.ID == OBJECT_VERTICES):' + temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + num_verts = struct.unpack('<H', temp_data)[0] + new_chunk.bytes_read += 2 # print 'number of verts: ', num_verts def getvert(): - temp_data= unpack('<3f', file.read(STRUCT_SIZE_3FLOAT)) + temp_data = struct.unpack('<3f', file.read(STRUCT_SIZE_3FLOAT)) new_chunk.bytes_read += STRUCT_SIZE_3FLOAT #12: 3 floats x 4 bytes each return temp_data #contextMesh.verts.extend( [Vector(),] ) # DUMMYVERT! - remove when blenders internals are fixed. - contextMesh_vertls= [getvert() for i in xrange(num_verts)] + contextMesh_vertls = [getvert() for i in range(num_verts)] #print 'object verts: bytes read: ', new_chunk.bytes_read - elif (new_chunk.ID==OBJECT_FACES): - # print 'elif (new_chunk.ID==OBJECT_FACES):' - temp_data= file.read(STRUCT_SIZE_UNSIGNED_SHORT) - num_faces= unpack('<H', temp_data)[0] - new_chunk.bytes_read+= 2 + elif (new_chunk.ID == OBJECT_FACES): + # print 'elif (new_chunk.ID == OBJECT_FACES):' + temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + num_faces = struct.unpack('<H', temp_data)[0] + new_chunk.bytes_read += 2 #print 'number of faces: ', num_faces def getface(): # print '\ngetting a face' - temp_data= file.read(STRUCT_SIZE_4UNSIGNED_SHORT) - new_chunk.bytes_read+= STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each - v1,v2,v3,dummy= unpack('<4H', temp_data) + temp_data = file.read(STRUCT_SIZE_4UNSIGNED_SHORT) + new_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each + v1,v2,v3,dummy = struct.unpack('<4H', temp_data) return v1, v2, v3 - contextMesh_facels= [ getface() for i in xrange(num_faces) ] + contextMesh_facels = [ getface() for i in range(num_faces) ] - elif (new_chunk.ID==OBJECT_MATERIAL): - # print 'elif (new_chunk.ID==OBJECT_MATERIAL):' - material_name= read_string(file) + elif (new_chunk.ID == OBJECT_MATERIAL): + # print 'elif (new_chunk.ID == OBJECT_MATERIAL):' + material_name = read_string(file) new_chunk.bytes_read += len(material_name)+1 # remove 1 null character. - temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) - num_faces_using_mat = unpack('<H', temp_data)[0] + temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + num_faces_using_mat = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT def getmat(): - temp_data= file.read(STRUCT_SIZE_UNSIGNED_SHORT) - new_chunk.bytes_read+= STRUCT_SIZE_UNSIGNED_SHORT - return unpack('<H', temp_data)[0] + temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT + return struct.unpack('<H', temp_data)[0] - contextMeshMaterials[material_name]= [ getmat() for i in xrange(num_faces_using_mat) ] + contextMeshMaterials[material_name]= [ getmat() for i in range(num_faces_using_mat) ] #look up the material in all the materials - elif (new_chunk.ID==OBJECT_UV): - temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) - num_uv=unpack('<H', temp_data)[0] - new_chunk.bytes_read+= 2 + elif (new_chunk.ID == OBJECT_UV): + temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + num_uv = struct.unpack('<H', temp_data)[0] + new_chunk.bytes_read += 2 def getuv(): - temp_data=file.read(STRUCT_SIZE_2FLOAT) + temp_data = file.read(STRUCT_SIZE_2FLOAT) new_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each - return Vector( unpack('<2f', temp_data) ) + return Mathutils.Vector( struct.unpack('<2f', temp_data) ) +# return Vector( struct.unpack('<2f', temp_data) ) - contextMeshUV= [ getuv() for i in xrange(num_uv) ] + contextMeshUV = [ getuv() for i in range(num_uv) ] - elif (new_chunk.ID== OBJECT_TRANS_MATRIX): + elif (new_chunk.ID == OBJECT_TRANS_MATRIX): # How do we know the matrix size? 54 == 4x4 48 == 4x3 - temp_data=file.read(STRUCT_SIZE_4x3MAT) - data= list( unpack('<ffffffffffff', temp_data) ) + temp_data = file.read(STRUCT_SIZE_4x3MAT) + data = list( struct.unpack('<ffffffffffff', temp_data) ) new_chunk.bytes_read += STRUCT_SIZE_4x3MAT - contextMatrix_rot= Blender.Mathutils.Matrix(\ + contextMatrix_rot = Mathutils.Matrix(\ +# contextMatrix_rot = Blender.Mathutils.Matrix(\ data[:3] + [0],\ data[3:6] + [0],\ data[6:9] + [0],\ @@ -749,7 +860,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): ''' - contextMatrix_rot= Blender.Mathutils.Matrix(\ + contextMatrix_rot = Blender.Mathutils.Matrix(\ data[:3] + [0],\ data[3:6] + [0],\ data[6:9] + [0],\ @@ -757,7 +868,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): ''' ''' - contextMatrix_rot= Blender.Mathutils.Matrix(\ + contextMatrix_rot = Blender.Mathutils.Matrix(\ data[:3] ,\ data[3:6],\ data[6:9]) @@ -769,7 +880,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): for j in xrange(4): for i in xrange(3): contextMatrix_rot[j][i] = data[m] - m+=1 + m += 1 contextMatrix_rot[0][3]=0; contextMatrix_rot[1][3]=0; @@ -790,82 +901,87 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): #contextMatrix = contextMatrix * tx #contextMatrix = contextMatrix *tx - elif (new_chunk.ID==MAT_MAP_FILENAME): - texture_name=read_string(file) + elif (new_chunk.ID == MAT_MAP_FILENAME): + texture_name = read_string(file) try: TEXTURE_DICT[contextMaterial.name] except: - #img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) - img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) + #img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) + img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname) +# img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH) - new_chunk.bytes_read+= len(texture_name)+1 #plus one for the null character that gets removed + new_chunk.bytes_read += len(texture_name)+1 #plus one for the null character that gets removed else: #(new_chunk.ID!=VERSION or new_chunk.ID!=OBJECTINFO or new_chunk.ID!=OBJECT or new_chunk.ID!=MATERIAL): # print 'skipping to end of this chunk' - buffer_size=new_chunk.length-new_chunk.bytes_read + buffer_size = new_chunk.length - new_chunk.bytes_read binary_format='%ic' % buffer_size - temp_data=file.read(calcsize(binary_format)) - new_chunk.bytes_read+=buffer_size + temp_data = file.read(struct.calcsize(binary_format)) + new_chunk.bytes_read += buffer_size #update the previous chunk bytes read # print 'previous_chunk.bytes_read += new_chunk.bytes_read' # print previous_chunk.bytes_read, new_chunk.bytes_read previous_chunk.bytes_read += new_chunk.bytes_read - ## print 'Bytes left in this chunk: ', previous_chunk.length-previous_chunk.bytes_read + ## print 'Bytes left in this chunk: ', previous_chunk.length - previous_chunk.bytes_read # FINISHED LOOP # There will be a number of objects still not added if contextMesh_facels != None: putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) -def load_3ds(filename, PREF_UI= True): - global FILENAME, SCN_OBJECTS - - if BPyMessages.Error_NoFile(filename): - return - - print '\n\nImporting 3DS: "%s"' % (Blender.sys.expandpath(filename)) +def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, APPLY_MATRIX=False): + global FILENAME, SCN +# global FILENAME, SCN_OBJECTS + + # XXX +# if BPyMessages.Error_NoFile(filename): +# return - time1= Blender.sys.time() + print('\n\nImporting 3DS: "%s"' % (filename)) +# print('\n\nImporting 3DS: "%s"' % (Blender.sys.expandpath(filename))) + + time1 = time.clock() +# time1 = Blender.sys.time() - FILENAME=filename - current_chunk=chunk() + FILENAME = filename + current_chunk = chunk() - file=open(filename,'rb') + file = open(filename,'rb') #here we go! # print 'reading the first chunk' read_chunk(file, current_chunk) if (current_chunk.ID!=PRIMARY): - print '\tFatal Error: Not a valid 3ds file: ', filename + print('\tFatal Error: Not a valid 3ds file: ', filename) file.close() return - # IMPORT_AS_INSTANCE= Blender.Draw.Create(0) - IMPORT_CONSTRAIN_BOUNDS= Blender.Draw.Create(10.0) - IMAGE_SEARCH= Blender.Draw.Create(1) - APPLY_MATRIX= Blender.Draw.Create(0) + # IMPORT_AS_INSTANCE = Blender.Draw.Create(0) +# IMPORT_CONSTRAIN_BOUNDS = Blender.Draw.Create(10.0) +# IMAGE_SEARCH = Blender.Draw.Create(1) +# APPLY_MATRIX = Blender.Draw.Create(0) # Get USER Options - pup_block= [\ - ('Size Constraint:', IMPORT_CONSTRAIN_BOUNDS, 0.0, 1000.0, 'Scale the model by 10 until it reacehs the size constraint. Zero Disables.'),\ - ('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\ - ('Transform Fix', APPLY_MATRIX, 'Workaround for object transformations importing incorrectly'),\ - #('Group Instance', IMPORT_AS_INSTANCE, 'Import objects into a new scene and group, creating an instance in the current scene.'),\ - ] +# pup_block = [\ +# ('Size Constraint:', IMPORT_CONSTRAIN_BOUNDS, 0.0, 1000.0, 'Scale the model by 10 until it reacehs the size constraint. Zero Disables.'),\ +# ('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\ +# ('Transform Fix', APPLY_MATRIX, 'Workaround for object transformations importing incorrectly'),\ +# #('Group Instance', IMPORT_AS_INSTANCE, 'Import objects into a new scene and group, creating an instance in the current scene.'),\ +# ] - if PREF_UI: - if not Blender.Draw.PupBlock('Import 3DS...', pup_block): - return +# if PREF_UI: +# if not Blender.Draw.PupBlock('Import 3DS...', pup_block): +# return - Blender.Window.WaitCursor(1) +# Blender.Window.WaitCursor(1) - IMPORT_CONSTRAIN_BOUNDS= IMPORT_CONSTRAIN_BOUNDS.val - # IMPORT_AS_INSTANCE= IMPORT_AS_INSTANCE.val - IMAGE_SEARCH = IMAGE_SEARCH.val - APPLY_MATRIX = APPLY_MATRIX.val +# IMPORT_CONSTRAIN_BOUNDS = IMPORT_CONSTRAIN_BOUNDS.val +# # IMPORT_AS_INSTANCE = IMPORT_AS_INSTANCE.val +# IMAGE_SEARCH = IMAGE_SEARCH.val +# APPLY_MATRIX = APPLY_MATRIX.val if IMPORT_CONSTRAIN_BOUNDS: BOUNDS_3DS[:]= [1<<30, 1<<30, 1<<30, -1<<30, -1<<30, -1<<30] @@ -874,96 +990,103 @@ def load_3ds(filename, PREF_UI= True): ##IMAGE_SEARCH - scn= bpy.data.scenes.active - SCN_OBJECTS = scn.objects - SCN_OBJECTS.selected = [] # de select all + scn = context.scene +# scn = bpy.data.scenes.active + SCN = scn +# SCN_OBJECTS = scn.objects +# SCN_OBJECTS.selected = [] # de select all - importedObjects= [] # Fill this list with objects + importedObjects = [] # Fill this list with objects process_next_chunk(file, current_chunk, importedObjects, IMAGE_SEARCH) # Link the objects into this scene. - # Layers= scn.Layers + # Layers = scn.Layers # REMOVE DUMMYVERT, - remove this in the next release when blenders internal are fixed. - for ob in importedObjects: - if ob.type=='Mesh': - me= ob.getData(mesh=1) - me.verts.delete([me.verts[0],]) - if not APPLY_MATRIX: - me.transform(ob.matrixWorld.copy().invert()) +# for ob in importedObjects: +# if ob.type == 'MESH': +# # if ob.type=='Mesh': +# me = ob.getData(mesh=1) +# me.verts.delete([me.verts[0],]) +# if not APPLY_MATRIX: +# me.transform(ob.matrixWorld.copy().invert()) # Done DUMMYVERT """ if IMPORT_AS_INSTANCE: - name= filename.split('\\')[-1].split('/')[-1] + name = filename.split('\\')[-1].split('/')[-1] # Create a group for this import. - group_scn= Scene.New(name) + group_scn = Scene.New(name) for ob in importedObjects: group_scn.link(ob) # dont worry about the layers - grp= Blender.Group.New(name) - grp.objects= importedObjects + grp = Blender.Group.New(name) + grp.objects = importedObjects - grp_ob= Object.New('Empty', name) - grp_ob.enableDupGroup= True - grp_ob.DupGroup= grp + grp_ob = Object.New('Empty', name) + grp_ob.enableDupGroup = True + grp_ob.DupGroup = grp scn.link(grp_ob) - grp_ob.Layers= Layers - grp_ob.sel= 1 + grp_ob.Layers = Layers + grp_ob.sel = 1 else: # Select all imported objects. for ob in importedObjects: scn.link(ob) - ob.Layers= Layers - ob.sel= 1 + ob.Layers = Layers + ob.sel = 1 """ - if IMPORT_CONSTRAIN_BOUNDS!=0.0: + if 0: +# if IMPORT_CONSTRAIN_BOUNDS!=0.0: # Set bounds from objecyt bounding box for ob in importedObjects: - if ob.type=='Mesh': + if ob.type == 'MESH': +# if ob.type=='Mesh': ob.makeDisplayList() # Why dosnt this update the bounds? for v in ob.getBoundBox(): for i in (0,1,2): if v[i] < BOUNDS_3DS[i]: BOUNDS_3DS[i]= v[i] # min - if v[i] > BOUNDS_3DS[i+3]: - BOUNDS_3DS[i+3]= v[i] # min + if v[i] > 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]) + 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. + 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 = 1.0 + while (max_axis * SCALE) > IMPORT_CONSTRAIN_BOUNDS: SCALE/=10 # SCALE Matrix - SCALE_MAT= Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) + 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) + ob.setMatrix(ob.matrixWorld * SCALE_MAT) # Done constraining to bounds. # Select all new objects. - print 'finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1)) + 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) +# 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') +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) @@ -973,14 +1096,14 @@ if __name__=='__main__' and not DEBUG: else: import os # DEBUG ONLY - TIME= Blender.sys.time() + 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 = 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] @@ -998,10 +1121,47 @@ else: #_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 = 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) diff --git a/release/scripts/io/import_obj.py b/release/scripts/io/import_obj.py index 81230bfcf03..a762005ae7d 100644 --- a/release/scripts/io/import_obj.py +++ b/release/scripts/io/import_obj.py @@ -9,7 +9,7 @@ Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.' __author__= "Campbell Barton", "Jiri Hnidek", "Paolo Ciccone" __url__= ['http://wiki.blender.org/index.php/Scripts/Manual/Import/wavefront_obj', 'blender.org', 'blenderartists.org'] -__version__= "2.13" +__version__= "2.11" __bpydoc__= """\ This script imports a Wavefront OBJ files to Blender. @@ -21,8 +21,7 @@ Note, This loads mesh objects and materials only, nurbs and curves are not suppo # ***** BEGIN GPL LICENSE BLOCK ***** # -# Script copyright (C) Campbell J Barton 2007-2009 -# - V2.12- bspline import/export added (funded by PolyDimensions GmbH) +# Script copyright (C) Campbell J Barton 2007 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -41,14 +40,19 @@ Note, This loads mesh objects and materials only, nurbs and curves are not suppo # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- -from Blender import Mesh, Draw, Window, Texture, Material, sys +import os +import time import bpy -import BPyMesh -import BPyImage -import BPyMessages +import Mathutils +import Geometry -try: import os -except: os= False +# from Blender import Mesh, Draw, Window, Texture, Material, sys +# # import BPyMesh +# import BPyImage +# import BPyMessages + +# try: import os +# except: os= False # Generic path functions def stripFile(path): @@ -56,7 +60,8 @@ def stripFile(path): lastSlash= max(path.rfind('\\'), path.rfind('/')) if lastSlash != -1: path= path[:lastSlash] - return '%s%s' % (path, sys.sep) + return '%s%s' % (path, os.sep) +# return '%s%s' % (path, sys.sep) def stripPath(path): '''Strips the slashes from the back of a string''' @@ -71,7 +76,215 @@ def stripExt(name): # name is a string return name # end path funcs +def unpack_list(list_of_tuples): + l = [] + for t in list_of_tuples: + l.extend(t) + return l + +# same as above except that it adds 0 for triangle faces +def unpack_face_list(list_of_tuples): + l = [] + for t in list_of_tuples: + face = [i for i in t] + if len(face) != 3 and len(face) != 4: + raise RuntimeError("{0} vertices in face.".format(len(face))) + + # rotate indices if the 4th is 0 + if len(face) == 4 and face[3] == 0: + face = [face[3], face[0], face[1], face[2]] + + if len(face) == 3: + face.append(0) + + l.extend(face) + + return l + +def BPyMesh_ngon(from_data, indices, PREF_FIX_LOOPS= True): + ''' + Takes a polyline of indices (fgon) + and returns a list of face indicie lists. + Designed to be used for importers that need indices for an fgon to create from existing verts. + + from_data: either a mesh, or a list/tuple of vectors. + indices: a list of indicies to use this list is the ordered closed polyline to fill, and can be a subset of the data given. + PREF_FIX_LOOPS: If this is enabled polylines that use loops to make multiple polylines are delt with correctly. + ''' + + if not set: # Need sets for this, otherwise do a normal fill. + PREF_FIX_LOOPS= False + + Vector= Mathutils.Vector + if not indices: + return [] + + # return [] + def rvec(co): return round(co.x, 6), round(co.y, 6), round(co.z, 6) + def mlen(co): return abs(co[0])+abs(co[1])+abs(co[2]) # manhatten length of a vector, faster then length + + def vert_treplet(v, i): + return v, rvec(v), i, mlen(v) + + def ed_key_mlen(v1, v2): + if v1[3] > v2[3]: + return v2[1], v1[1] + else: + return v1[1], v2[1] + + + if not PREF_FIX_LOOPS: + ''' + Normal single concave loop filling + ''' + if type(from_data) in (tuple, list): + verts= [Vector(from_data[i]) for ii, i in enumerate(indices)] + else: + verts= [from_data.verts[i].co for ii, i in enumerate(indices)] + + for i in range(len(verts)-1, 0, -1): # same as reversed(xrange(1, len(verts))): + if verts[i][1]==verts[i-1][0]: + verts.pop(i-1) + + fill= Geometry.PolyFill([verts]) + + else: + ''' + Seperate this loop into multiple loops be finding edges that are used twice + This is used by lightwave LWO files a lot + ''' + + if type(from_data) in (tuple, list): + verts= [vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices)] + else: + verts= [vert_treplet(from_data.verts[i].co, ii) for ii, i in enumerate(indices)] + + edges= [(i, i-1) for i in range(len(verts))] + if edges: + edges[0]= (0,len(verts)-1) + + if not verts: + return [] + + + edges_used= set() + edges_doubles= set() + # We need to check if any edges are used twice location based. + for ed in edges: + edkey= ed_key_mlen(verts[ed[0]], verts[ed[1]]) + if edkey in edges_used: + edges_doubles.add(edkey) + else: + edges_used.add(edkey) + + # Store a list of unconnected loop segments split by double edges. + # will join later + loop_segments= [] + + v_prev= verts[0] + context_loop= [v_prev] + loop_segments= [context_loop] + + for v in verts: + if v!=v_prev: + # Are we crossing an edge we removed? + if ed_key_mlen(v, v_prev) in edges_doubles: + context_loop= [v] + loop_segments.append(context_loop) + else: + if context_loop and context_loop[-1][1]==v[1]: + #raise "as" + pass + else: + context_loop.append(v) + + v_prev= v + # Now join loop segments + + def join_seg(s1,s2): + if s2[-1][1]==s1[0][1]: # + s1,s2= s2,s1 + elif s1[-1][1]==s2[0][1]: + pass + else: + return False + + # If were stuill here s1 and s2 are 2 segments in the same polyline + s1.pop() # remove the last vert from s1 + s1.extend(s2) # add segment 2 to segment 1 + + if s1[0][1]==s1[-1][1]: # remove endpoints double + s1.pop() + + s2[:]= [] # Empty this segment s2 so we dont use it again. + return True + + joining_segments= True + while joining_segments: + joining_segments= False + segcount= len(loop_segments) + + for j in range(segcount-1, -1, -1): #reversed(range(segcount)): + seg_j= loop_segments[j] + if seg_j: + for k in range(j-1, -1, -1): # reversed(range(j)): + if not seg_j: + break + seg_k= loop_segments[k] + + if seg_k and join_seg(seg_j, seg_k): + joining_segments= True + + loop_list= loop_segments + + for verts in loop_list: + while verts and verts[0][1]==verts[-1][1]: + verts.pop() + + loop_list= [verts for verts in loop_list if len(verts)>2] + # DONE DEALING WITH LOOP FIXING + + + # vert mapping + vert_map= [None]*len(indices) + ii=0 + for verts in loop_list: + if len(verts)>2: + for i, vert in enumerate(verts): + vert_map[i+ii]= vert[2] + ii+=len(verts) + + fill= Geometry.PolyFill([ [v[0] for v in loop] for loop in loop_list ]) + #draw_loops(loop_list) + #raise 'done loop' + # map to original indicies + fill= [[vert_map[i] for i in reversed(f)] for f in fill] + + + if not fill: + print('Warning Cannot scanfill, fallback on a triangle fan.') + fill= [ [0, i-1, i] for i in range(2, len(indices)) ] + else: + # Use real scanfill. + # See if its flipped the wrong way. + flip= None + for fi in fill: + if flip != None: + break + for i, vi in enumerate(fi): + if vi==0 and fi[i-1]==1: + flip= False + break + elif vi==1 and fi[i-1]==0: + flip= True + break + + if not flip: + for i, fi in enumerate(fill): + fill[i]= tuple([ii for ii in reversed(fi)]) + + return fill def line_value(line_split): ''' @@ -88,22 +301,47 @@ def line_value(line_split): elif length > 2: return ' '.join( line_split[1:] ) +# limited replacement for BPyImage.comprehensiveImageLoad +def load_image(imagepath, dirname): + + if os.path.exists(imagepath): + return bpy.data.add_image(imagepath) + + variants = [os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] + + for path in variants: + if os.path.exists(path): + return bpy.data.add_image(path) + else: + print(path, "doesn't exist") + + # TODO comprehensiveImageLoad also searched in bpy.config.textureDir + return None + def obj_image_load(imagepath, DIR, IMAGE_SEARCH): - ''' - Mainly uses comprehensiveImageLoad - but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores. - ''' - + if '_' in imagepath: - image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) - if image: return image - # Did the exporter rename the image? - image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) + image= load_image(imagepath.replace('_', ' '), DIR) if image: return image - - # Return an image, placeholder if it dosnt exist - image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH) - return image + + return load_image(imagepath, DIR) + +# def obj_image_load(imagepath, DIR, IMAGE_SEARCH): +# ''' +# Mainly uses comprehensiveImageLoad +# but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores. +# ''' + +# if '_' in imagepath: +# image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) +# if image: return image +# # Did the exporter rename the image? +# image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH) +# if image: return image + +# # Return an image, placeholder if it dosnt exist +# image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH) +# return image def create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH): @@ -117,69 +355,82 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ # This function sets textures defined in .mtl file # #==================================================================================# def load_material_image(blender_material, context_material_name, imagepath, type): - - texture= bpy.data.textures.new(type) - texture.setType('Image') + + texture= bpy.data.add_texture(type) + texture.type= 'IMAGE' +# texture= bpy.data.textures.new(type) +# texture.setType('Image') # Absolute path - c:\.. etc would work here image= obj_image_load(imagepath, DIR, IMAGE_SEARCH) - has_data = image.has_data - texture.image = image + has_data = image.has_data if image else False - if not has_data: - try: - # first time using this image. We need to load it first - image.glLoad() - except: - # probably the image is crashed - pass - else: - has_data = image.has_data + if image: + texture.image = image # Adds textures for materials (rendering) if type == 'Kd': if has_data and image.depth == 32: # Image has alpha - blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL | Texture.MapTo.ALPHA) - texture.setImageFlags('MipMap', 'InterPol', 'UseAlpha') - blender_material.mode |= Material.Modes.ZTRANSP + + # XXX bitmask won't work? + blender_material.add_texture(texture, "UV", ("COLOR", "ALPHA")) + texture.mipmap = True + texture.interpolation = True + texture.use_alpha = True + blender_material.z_transparency = True blender_material.alpha = 0.0 + +# blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL | Texture.MapTo.ALPHA) +# texture.setImageFlags('MipMap', 'InterPol', 'UseAlpha') +# blender_material.mode |= Material.Modes.ZTRANSP +# blender_material.alpha = 0.0 else: - blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) + blender_material.add_texture(texture, "UV", "COLOR") +# blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL) # adds textures to faces (Textured/Alt-Z mode) # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func. unique_material_images[context_material_name]= image, has_data # set the texface image elif type == 'Ka': - blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API + blender_material.add_texture(texture, "UV", "AMBIENT") +# blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API elif type == 'Ks': - blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC) + blender_material.add_texture(texture, "UV", "SPECULARITY") +# blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC) elif type == 'Bump': - blender_material.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR) + blender_material.add_texture(texture, "UV", "NORMAL") +# blender_material.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR) elif type == 'D': - blender_material.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA) - blender_material.mode |= Material.Modes.ZTRANSP + blender_material.add_texture(texture, "UV", "ALPHA") + blender_material.z_transparency = True blender_material.alpha = 0.0 +# blender_material.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA) +# blender_material.mode |= Material.Modes.ZTRANSP +# blender_material.alpha = 0.0 # Todo, unset deffuse material alpha if it has an alpha channel elif type == 'refl': - blender_material.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF) + blender_material.add_texture(texture, "UV", "REFLECTION") +# blender_material.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF) # Add an MTL with the same name as the obj if no MTLs are spesified. temp_mtl= stripExt(stripPath(filepath))+ '.mtl' - - if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs: - material_libs.append( temp_mtl ) + + if os.path.exists(DIR + temp_mtl) and temp_mtl not in material_libs: +# if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs: + material_libs.append( temp_mtl ) del temp_mtl #Create new materials for name in unique_materials: # .keys() if name != None: - unique_materials[name]= bpy.data.materials.new(name) + unique_materials[name]= bpy.data.add_material(name) +# unique_materials[name]= bpy.data.materials.new(name) unique_material_images[name]= None, False # assign None to all material images to start with, add to later. unique_materials[None]= None @@ -187,7 +438,8 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ for libname in material_libs: mtlpath= DIR + libname - if not sys.exists(mtlpath): + if not os.path.exists(mtlpath): +# if not sys.exists(mtlpath): #print '\tError Missing MTL: "%s"' % mtlpath pass else: @@ -197,7 +449,7 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ for line in mtl: #.xreadlines(): if line.startswith('newmtl'): context_material_name= line_value(line.split()) - if unique_materials.has_key(context_material_name): + if context_material_name in unique_materials: context_material = unique_materials[ context_material_name ] else: context_material = None @@ -207,18 +459,23 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ line_split= line.split() line_lower= line.lower().lstrip() if line_lower.startswith('ka'): - context_material.setMirCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + context_material.mirror_color = (float(line_split[1]), float(line_split[2]), float(line_split[3])) +# context_material.setMirCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) elif line_lower.startswith('kd'): - context_material.setRGBCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + context_material.diffuse_color = (float(line_split[1]), float(line_split[2]), float(line_split[3])) +# context_material.setRGBCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) elif line_lower.startswith('ks'): - context_material.setSpecCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) + context_material.specular_color = (float(line_split[1]), float(line_split[2]), float(line_split[3])) +# context_material.setSpecCol((float(line_split[1]), float(line_split[2]), float(line_split[3]))) elif line_lower.startswith('ns'): - context_material.setHardness( int((float(line_split[1])*0.51)) ) + context_material.specular_hardness = int((float(line_split[1])*0.51)) +# context_material.setHardness( int((float(line_split[1])*0.51)) ) elif line_lower.startswith('ni'): # Refraction index - context_material.setIOR( max(1, min(float(line_split[1]), 3))) # Between 1 and 3 + context_material.ior = max(1, min(float(line_split[1]), 3)) +# context_material.setIOR( max(1, min(float(line_split[1]), 3))) # Between 1 and 3 elif line_lower.startswith('d') or line_lower.startswith('tr'): - context_material.setAlpha(float(line_split[1])) - context_material.mode |= Material.Modes.ZTRANSP + context_material.alpha = float(line_split[1]) +# context_material.setAlpha(float(line_split[1])) elif line_lower.startswith('map_ka'): img_filepath= line_value(line.split()) if img_filepath: @@ -322,14 +579,14 @@ def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, face_vert_loc_indicies[enum] = vert_remap[i] # remap to the local index matname= face[2] - if matname and not unique_materials_split.has_key(matname): + if matname and matname not in unique_materials_split: unique_materials_split[matname] = unique_materials[matname] faces_split.append(face) # remove one of the itemas and reorder - return [(value[0], value[1], value[2], key_to_name(key)) for key, value in face_split_dict.iteritems()] + return [(value[0], value[1], value[2], key_to_name(key)) for key, value in list(face_split_dict.items())] def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname): @@ -342,7 +599,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if unique_smooth_groups: sharp_edges= {} - smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in unique_smooth_groups.iterkeys() ]) + smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in list(unique_smooth_groups.keys()) ]) context_smooth_group_old= -1 # Split fgons into tri's @@ -353,7 +610,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l context_object= None # reverse loop through face indicies - for f_idx in xrange(len(faces)-1, -1, -1): + for f_idx in range(len(faces)-1, -1, -1): face_vert_loc_indicies,\ face_vert_tex_indicies,\ @@ -370,7 +627,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if CREATE_EDGES: # generators are better in python 2.4+ but can't be used in 2.3 # edges.extend( (face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1) ) - edges.extend( [(face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1)] ) + edges.extend( [(face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in range(len_face_vert_loc_indicies-1)] ) faces.pop(f_idx) else: @@ -382,7 +639,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l edge_dict= smooth_group_users[context_smooth_group] context_smooth_group_old= context_smooth_group - for i in xrange(len_face_vert_loc_indicies): + for i in range(len_face_vert_loc_indicies): i1= face_vert_loc_indicies[i] i2= face_vert_loc_indicies[i-1] if i1>i2: i1,i2= i2,i1 @@ -395,7 +652,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # FGons into triangles if has_ngons and len_face_vert_loc_indicies > 4: - ngon_face_indices= BPyMesh.ngon(verts_loc, face_vert_loc_indicies) + ngon_face_indices= BPyMesh_ngon(verts_loc, face_vert_loc_indicies) faces.extend(\ [(\ [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\ @@ -420,7 +677,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l except KeyError: edge_users[i1,i2]= 1 - for key, users in edge_users.iteritems(): + for key, users in edge_users.items(): if users>1: fgon_edges[key]= None @@ -430,8 +687,8 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # Build sharp edges if unique_smooth_groups: - for edge_dict in smooth_group_users.itervalues(): - for key, users in edge_dict.iteritems(): + for edge_dict in list(smooth_group_users.values()): + for key, users in list(edge_dict.items()): if users==1: # This edge is on the boundry of a group sharp_edges[key]= None @@ -441,25 +698,39 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l materials= [None] * len(unique_materials) - for name, index in material_mapping.iteritems(): + for name, index in list(material_mapping.items()): materials[index]= unique_materials[name] - - me= bpy.data.meshes.new(dataname) - - me.materials= materials[0:16] # make sure the list isnt too big. + + me= bpy.data.add_mesh(dataname) +# me= bpy.data.meshes.new(dataname) + + # make sure the list isnt too big + for material in materials[0:16]: + me.add_material(material) +# me.materials= materials[0:16] # make sure the list isnt too big. #me.verts.extend([(0,0,0)]) # dummy vert - me.verts.extend(verts_loc) - - face_mapping= me.faces.extend([f[0] for f in faces], indexList=True) + + me.add_geometry(len(verts_loc), 0, len(faces)) + + # verts_loc is a list of (x, y, z) tuples + me.verts.foreach_set("co", unpack_list(verts_loc)) +# me.verts.extend(verts_loc) + + # faces is a list of (vert_indices, texco_indices, ...) tuples + # XXX faces should contain either 3 or 4 verts + # XXX no check for valid face indices + me.faces.foreach_set("verts_raw", unpack_face_list([f[0] for f in faces])) +# face_mapping= me.faces.extend([f[0] for f in faces], indexList=True) if verts_tex and me.faces: - me.faceUV= 1 + me.add_uv_texture() +# me.faceUV= 1 # TEXMODE= Mesh.FaceModes['TEX'] context_material_old= -1 # avoid a dict lookup mat= 0 # rare case it may be un-initialized. me_faces= me.faces - ALPHA= Mesh.FaceTranspModes.ALPHA +# ALPHA= Mesh.FaceTranspModes.ALPHA for i, face in enumerate(faces): if len(face[0]) < 2: @@ -468,9 +739,14 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if CREATE_EDGES: edges.append(face[0]) else: - face_index_map= face_mapping[i] - if face_index_map!=None: # None means the face wasnt added - blender_face= me_faces[face_index_map] +# face_index_map= face_mapping[i] + + # since we use foreach_set to add faces, all of them are added + if 1: +# if face_index_map!=None: # None means the face wasnt added + + blender_face = me.faces[i] +# blender_face= me_faces[face_index_map] face_vert_loc_indicies,\ face_vert_tex_indicies,\ @@ -489,17 +765,24 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if mat>15: mat= 15 context_material_old= context_material - - blender_face.mat= mat + + blender_face.material_index= mat +# blender_face.mat= mat - if verts_tex: + if verts_tex: + + blender_tface= me.uv_textures[0].data[i] + if context_material: image, has_data= unique_material_images[context_material] if image: # Can be none if the material dosnt have an image. - blender_face.image= image - if has_data and image.depth == 32: - blender_face.transp |= ALPHA + blender_tface.image= image +# blender_face.image= image + if has_data: +# if has_data and image.depth == 32: + blender_tface.transp = 'ALPHA' +# blender_face.transp |= ALPHA # BUG - Evil eekadoodle problem where faces that have vert index 0 location at 3 or 4 are shuffled. if len(face_vert_loc_indicies)==4: @@ -511,43 +794,80 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # END EEEKADOODLE FIX # assign material, uv's and image - for ii, uv in enumerate(blender_face.uv): - uv.x, uv.y= verts_tex[face_vert_tex_indicies[ii]] + blender_tface.uv1= verts_tex[face_vert_tex_indicies[0]] + blender_tface.uv2= verts_tex[face_vert_tex_indicies[1]] + blender_tface.uv3= verts_tex[face_vert_tex_indicies[2]] + + if blender_face.verts[3] != 0: + blender_tface.uv4= verts_tex[face_vert_tex_indicies[3]] + +# for ii, uv in enumerate(blender_face.uv): +# uv.x, uv.y= verts_tex[face_vert_tex_indicies[ii]] del me_faces - del ALPHA - - # Add edge faces. - me_edges= me.edges - if CREATE_FGONS and fgon_edges: - FGON= Mesh.EdgeFlags.FGON - for ed in me.findEdges( fgon_edges.keys() ): - if ed!=None: - me_edges[ed].flag |= FGON - del FGON - - if unique_smooth_groups and sharp_edges: - SHARP= Mesh.EdgeFlags.SHARP - for ed in me.findEdges( sharp_edges.keys() ): - if ed!=None: - me_edges[ed].flag |= SHARP - del SHARP - +# del ALPHA + if CREATE_EDGES: - me_edges.extend( edges ) + + me.add_geometry(0, len(edges), 0) + + # edges should be a list of (a, b) tuples + me.edges.foreach_set("verts", unpack_list(edges)) +# me_edges.extend( edges ) - del me_edges +# del me_edges - me.calcNormals() + # Add edge faces. +# me_edges= me.edges + + def edges_match(e1, e2): + return (e1[0] == e2[0] and e1[1] == e2[1]) or (e1[0] == e2[1] and e1[1] == e2[0]) + + # XXX slow +# if CREATE_FGONS and fgon_edges: +# for fgon_edge in fgon_edges.keys(): +# for ed in me.edges: +# if edges_match(fgon_edge, ed.verts): +# ed.fgon = True + +# if CREATE_FGONS and fgon_edges: +# FGON= Mesh.EdgeFlags.FGON +# for ed in me.findEdges( fgon_edges.keys() ): +# if ed!=None: +# me_edges[ed].flag |= FGON +# del FGON + + # XXX slow +# if unique_smooth_groups and sharp_edges: +# for sharp_edge in sharp_edges.keys(): +# for ed in me.edges: +# if edges_match(sharp_edge, ed.verts): +# ed.sharp = True + +# if unique_smooth_groups and sharp_edges: +# SHARP= Mesh.EdgeFlags.SHARP +# for ed in me.findEdges( sharp_edges.keys() ): +# if ed!=None: +# me_edges[ed].flag |= SHARP +# del SHARP + + me.update() +# me.calcNormals() - ob= scn.objects.new(me) + ob= bpy.data.add_object("MESH", "Mesh") + ob.data= me + scn.add_object(ob) +# ob= scn.objects.new(me) new_objects.append(ob) # Create the vertex groups. No need to have the flag passed here since we test for the # content of the vertex_groups. If the user selects to NOT have vertex groups saved then # the following test will never run - for group_name, group_indicies in vertex_groups.iteritems(): - me.addVertGroup(group_name) - me.assignVertsToGroup(group_name, group_indicies,1.00, Mesh.AssignModes.REPLACE) + for group_name, group_indicies in vertex_groups.items(): + group= ob.add_vertex_group(group_name) +# me.addVertGroup(group_name) + for vertex_index in group_indicies: + ob.add_vertex_to_group(vertex_index, group, 1.0, 'REPLACE') +# me.assignVertsToGroup(group_name, group_indicies, 1.00, Mesh.AssignModes.REPLACE) def create_nurbs(scn, context_nurbs, vert_loc, new_objects): @@ -563,16 +883,16 @@ def create_nurbs(scn, context_nurbs, vert_loc, new_objects): cstype = context_nurbs.get('cstype', None) if cstype == None: - print '\tWarning, cstype not found' + print('\tWarning, cstype not found') return if cstype != 'bspline': - print '\tWarning, cstype is not supported (only bspline)' + print('\tWarning, cstype is not supported (only bspline)') return if not curv_idx: - print '\tWarning, curv argument empty or not set' + print('\tWarning, curv argument empty or not set') return if len(deg) > 1 or parm_v: - print '\tWarning, surfaces not supported' + print('\tWarning, surfaces not supported') return cu = bpy.data.curves.new(name, 'Curve') @@ -594,7 +914,7 @@ def create_nurbs(scn, context_nurbs, vert_loc, new_objects): # get for endpoint flag from the weighting if curv_range and len(parm_u) > deg[0]+1: do_endpoints = True - for i in xrange(deg[0]+1): + for i in range(deg[0]+1): if abs(parm_u[i]-curv_range[0]) > 0.0001: do_endpoints = False @@ -659,28 +979,30 @@ def get_float_func(filepath): return float def load_obj(filepath, - CLAMP_SIZE= 0.0, - CREATE_FGONS= True, - CREATE_SMOOTH_GROUPS= True, - CREATE_EDGES= True, - SPLIT_OBJECTS= True, - SPLIT_GROUPS= True, - SPLIT_MATERIALS= True, - ROTATE_X90= True, - IMAGE_SEARCH=True, - POLYGROUPS=False): + context, + CLAMP_SIZE= 0.0, + CREATE_FGONS= True, + CREATE_SMOOTH_GROUPS= True, + CREATE_EDGES= True, + SPLIT_OBJECTS= True, + SPLIT_GROUPS= True, + SPLIT_MATERIALS= True, + ROTATE_X90= True, + IMAGE_SEARCH=True, + POLYGROUPS=False): ''' Called by the user interface or another script. load_obj(path) - should give acceptable results. This function passes the file and sends the data off to be split into objects and then converted into mesh objects ''' - print '\nimporting obj "%s"' % filepath + print('\nimporting obj "%s"' % filepath) if SPLIT_OBJECTS or SPLIT_GROUPS or SPLIT_MATERIALS: POLYGROUPS = False - - time_main= sys.time() + + time_main= time.time() +# time_main= sys.time() verts_loc= [] verts_tex= [] @@ -717,8 +1039,9 @@ def load_obj(filepath, # so we need to know weather context_multi_line= '' - print '\tparsing obj file "%s"...' % filepath, - time_sub= sys.time() + print('\tparsing obj file "%s"...' % filepath) + time_sub= time.time() +# time_sub= sys.time() file= open(filepath, 'rU') for line in file: #.xreadlines(): @@ -926,70 +1249,77 @@ def load_obj(filepath, ''' file.close() - time_new= sys.time() - print '%.4f sec' % (time_new-time_sub) + time_new= time.time() +# time_new= sys.time() + print('%.4f sec' % (time_new-time_sub)) time_sub= time_new - print '\tloading materials and images...', + print('\tloading materials and images...') create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH) - - time_new= sys.time() - print '%.4f sec' % (time_new-time_sub) + + time_new= time.time() +# time_new= sys.time() + print('%.4f sec' % (time_new-time_sub)) time_sub= time_new if not ROTATE_X90: verts_loc[:] = [(v[0], v[2], -v[1]) for v in verts_loc] # deselect all - scn = bpy.data.scenes.active - scn.objects.selected = [] +# if context.selected_objects: +# bpy.ops.OBJECT_OT_select_all_toggle() + + scene = context.scene +# scn = bpy.data.scenes.active +# scn.objects.selected = [] new_objects= [] # put new objects here - print '\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ), + print('\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) )) # Split the mesh by objects/materials, may if SPLIT_OBJECTS or SPLIT_GROUPS: SPLIT_OB_OR_GROUP = True else: SPLIT_OB_OR_GROUP = False for verts_loc_split, faces_split, unique_materials_split, dataname in split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, SPLIT_MATERIALS): # Create meshes from the data, warning 'vertex_groups' wont support splitting - create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname) + create_mesh(scene, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname) # nurbs support - for context_nurbs in nurbs: - create_nurbs(scn, context_nurbs, verts_loc, new_objects) +# for context_nurbs in nurbs: +# create_nurbs(scn, context_nurbs, verts_loc, new_objects) axis_min= [ 1000000000]*3 axis_max= [-1000000000]*3 - if CLAMP_SIZE: - # Get all object bounds - for ob in new_objects: - for v in ob.getBoundBox(): - for axis, value in enumerate(v): - if axis_min[axis] > value: axis_min[axis]= value - if axis_max[axis] < value: axis_max[axis]= value +# if CLAMP_SIZE: +# # Get all object bounds +# for ob in new_objects: +# for v in ob.getBoundBox(): +# for axis, value in enumerate(v): +# if axis_min[axis] > value: axis_min[axis]= value +# if axis_max[axis] < value: axis_max[axis]= value - # Scale objects - max_axis= max(axis_max[0]-axis_min[0], axis_max[1]-axis_min[1], axis_max[2]-axis_min[2]) - scale= 1.0 +# # Scale objects +# max_axis= max(axis_max[0]-axis_min[0], axis_max[1]-axis_min[1], axis_max[2]-axis_min[2]) +# scale= 1.0 - while CLAMP_SIZE < max_axis * scale: - scale= scale/10.0 +# while CLAMP_SIZE < max_axis * scale: +# scale= scale/10.0 - for ob in new_objects: - ob.setSize(scale, scale, scale) +# for ob in new_objects: +# ob.setSize(scale, scale, scale) # Better rotate the vert locations #if not ROTATE_X90: # for ob in new_objects: # ob.RotX = -1.570796326794896558 + + time_new= time.time() +# time_new= sys.time() - time_new= sys.time() - - print '%.4f sec' % (time_new-time_sub) - print 'finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main)) + print('%.4f sec' % (time_new-time_sub)) + print('finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main))) DEBUG= True @@ -1082,14 +1412,14 @@ def load_obj_ui(filepath, BATCH_LOAD= False): def do_help(e,v): url = __url__[0] - print 'Trying to open web browser with documentation at this address...' - print '\t' + url + print('Trying to open web browser with documentation at this address...') + print('\t' + url) try: import webbrowser webbrowser.open(url) except: - print '...could not open a browser window.' + print('...could not open a browser window.') def obj_ui(): ui_x, ui_y = GLOBALS['MOUSE'] @@ -1199,11 +1529,11 @@ def load_obj_ui_batch(file): DEBUG= False -if __name__=='__main__' and not DEBUG: - if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT: - Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '') - else: - Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj') +# if __name__=='__main__' and not DEBUG: +# if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT: +# Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '') +# else: +# Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj') # For testing compatibility ''' @@ -1232,3 +1562,77 @@ else: ''' #load_obj('/test.obj') #load_obj('/fe/obj/mba1.obj') + + + +class IMPORT_OT_obj(bpy.types.Operator): + ''' + Operator documentation text, will be used for the operator tooltip and python docs. + ''' + __idname__ = "import.obj" + __label__ = "Import OBJ" + + # 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 OBJ file", maxlen= 1024, default= ""), + + bpy.props.BoolProperty(attr="CREATE_SMOOTH_GROUPS", name="Smooth Groups", description="Surround smooth groups by sharp edges", default= True), + bpy.props.BoolProperty(attr="CREATE_FGONS", name="NGons as FGons", description="Import faces with more then 4 verts as fgons", default= True), + bpy.props.BoolProperty(attr="CREATE_EDGES", name="Lines as Edges", description="Import lines and faces with 2 verts as edge", default= True), + bpy.props.BoolProperty(attr="SPLIT_OBJECTS", name="Object", description="Import OBJ Objects into Blender Objects", default= True), + bpy.props.BoolProperty(attr="SPLIT_GROUPS", name="Group", description="Import OBJ Groups into Blender Objects", default= True), + bpy.props.BoolProperty(attr="SPLIT_MATERIALS", name="Material", description="Import each material into a seperate mesh (Avoids > 16 per mesh error)", default= True), + # old comment: only used for user feedback + # disabled this option because in old code a handler for it disabled SPLIT* params, it's not passed to load_obj + # bpy.props.BoolProperty(attr="KEEP_VERT_ORDER", name="Keep Vert Order", description="Keep vert and face order, disables split options, enable for morph targets", default= True), + bpy.props.BoolProperty(attr="ROTATE_X90", name="-X90", description="Rotate X 90.", default= True), + bpy.props.FloatProperty(attr="CLAMP_SIZE", name="Clamp Scale", description="Clamp the size to this maximum (Zero to Disable)", min=0.01, max=1000.0, soft_min=0.0, soft_max=1000.0, default=0.0), + bpy.props.BoolProperty(attr="POLYGROUPS", name="Poly Groups", description="Import OBJ groups as vertex groups.", default= True), + bpy.props.BoolProperty(attr="IMAGE_SEARCH", name="Image Search", description="Search subdirs for any assosiated images (Warning, may be slow)", default= True), + ] + + ''' + def poll(self, context): + return True ''' + + def execute(self, context): + # print("Selected: " + context.active_object.name) + + load_obj(self.path, + context, + self.CLAMP_SIZE, + self.CREATE_FGONS, + self.CREATE_SMOOTH_GROUPS, + self.CREATE_EDGES, + self.SPLIT_OBJECTS, + self.SPLIT_GROUPS, + self.SPLIT_MATERIALS, + self.ROTATE_X90, + self.IMAGE_SEARCH, + self.POLYGROUPS) + + return ('FINISHED',) + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self.__operator__) + return ('RUNNING_MODAL',) + + +bpy.ops.add(IMPORT_OT_obj) + + +# NOTES (all line numbers refer to 2.4x import_obj.py, not this file) +# check later: line 489 +# can convert now: edge flags, edges: lines 508-528 +# ngon (uses python module BPyMesh): 384-414 +# nurbs: 947- +# NEXT clamp size: get bound box with RNA +# get back to l 140 (here) +# search image in bpy.config.textureDir - load_image +# replaced BPyImage.comprehensiveImageLoad with a simplified version that only checks additional directory specified, but doesn't search dirs recursively (obj_image_load) +# bitmask won't work? - 132 +# uses operator bpy.ops.OBJECT_OT_select_all_toggle() to deselect all (not necessary?) +# uses bpy.sys.time() |