Changeset View
Changeset View
Standalone View
Standalone View
io_scene_obj/export_obj.py
| Show All 15 Lines | |||||
| # | # | ||||
| # ##### END GPL LICENSE BLOCK ##### | # ##### END GPL LICENSE BLOCK ##### | ||||
| # <pep8 compliant> | # <pep8 compliant> | ||||
| import os | import os | ||||
| import bpy | import bpy | ||||
| import mathutils | from mathutils import Matrix, Vector, Color | ||||
| import bpy_extras.io_utils | from bpy_extras import io_utils, node_shader_utils | ||||
| from progress_report import ProgressReport, ProgressReportSubstep | from progress_report import ProgressReport, ProgressReportSubstep | ||||
| def name_compat(name): | def name_compat(name): | ||||
| if name is None: | if name is None: | ||||
| return 'None' | return 'None' | ||||
| else: | else: | ||||
| return name.replace(' ', '_') | return name.replace(' ', '_') | ||||
| def mesh_triangulate(me): | def mesh_triangulate(me): | ||||
| import bmesh | import bmesh | ||||
| bm = bmesh.new() | bm = bmesh.new() | ||||
| bm.from_mesh(me) | bm.from_mesh(me) | ||||
| bmesh.ops.triangulate(bm, faces=bm.faces) | bmesh.ops.triangulate(bm, faces=bm.faces) | ||||
| bm.to_mesh(me) | bm.to_mesh(me) | ||||
| bm.free() | bm.free() | ||||
| def write_mtl(scene, filepath, path_mode, copy_set, mtl_dict): | def write_mtl(scene, filepath, path_mode, copy_set, mtl_dict): | ||||
| from mathutils import Color, Vector | |||||
| world = scene.world | world = scene.world | ||||
| if world: | world_amb = Color((0.8, 0.8, 0.8)) | ||||
| world_amb = world.ambient_color | |||||
| else: | |||||
| world_amb = Color((0.0, 0.0, 0.0)) | |||||
| source_dir = os.path.dirname(bpy.data.filepath) | source_dir = os.path.dirname(bpy.data.filepath) | ||||
| dest_dir = os.path.dirname(filepath) | dest_dir = os.path.dirname(filepath) | ||||
| with open(filepath, "w", encoding="utf8", newline="\n") as f: | with open(filepath, "w", encoding="utf8", newline="\n") as f: | ||||
| fw = f.write | fw = f.write | ||||
| fw('# Blender MTL File: %r\n' % (os.path.basename(bpy.data.filepath) or "None")) | fw('# Blender MTL File: %r\n' % (os.path.basename(bpy.data.filepath) or "None")) | ||||
| fw('# Material Count: %i\n' % len(mtl_dict)) | fw('# Material Count: %i\n' % len(mtl_dict)) | ||||
| mtl_dict_values = list(mtl_dict.values()) | mtl_dict_values = list(mtl_dict.values()) | ||||
| mtl_dict_values.sort(key=lambda m: m[0]) | mtl_dict_values.sort(key=lambda m: m[0]) | ||||
| # Write material/image combinations we have used. | # Write material/image combinations we have used. | ||||
| # Using mtl_dict.values() directly gives un-predictable order. | # Using mtl_dict.values() directly gives un-predictable order. | ||||
| for mtl_mat_name, mat, face_img in mtl_dict_values: | for mtl_mat_name, mat in mtl_dict_values: | ||||
| # Get the Blender data for the material and the image. | # Get the Blender data for the material and the image. | ||||
| # Having an image named None will make a bug, dont do it :) | # Having an image named None will make a bug, dont do it :) | ||||
| fw('\nnewmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname | fw('\nnewmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname | ||||
| if mat: | mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat) if mat else None | ||||
| use_mirror = mat.raytrace_mirror.use and mat.raytrace_mirror.reflect_factor != 0.0 | |||||
| # convert from blenders spec to 0 - 1000 range. | if mat_wrap: | ||||
| if mat.specular_shader == 'WARDISO': | use_mirror = mat_wrap.metallic != 0.0 | ||||
| tspec = (0.4 - mat.specular_slope) / 0.0004 | use_transparency = mat_wrap.transmission != 0.0 | ||||
| else: | |||||
| tspec = (mat.specular_hardness - 1) / 0.51 | # Convert from principled roughness to 0 - 1000 specular range. | ||||
| fw('Ns %.6f\n' % tspec) | # XXX Basic linear conversion, what would be best-matching formula here? | ||||
| del tspec | fw('Ns %.6f\n' % ((1.0 - mat_wrap.roughness) * 1000)) | ||||
| # Ambient | # Ambient | ||||
| if use_mirror: | if use_mirror: | ||||
| fw('Ka %.6f %.6f %.6f\n' % (mat.raytrace_mirror.reflect_factor * mat.mirror_color)[:]) | fw('Ka %.6f %.6f %.6f\n' % (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic)) | ||||
| else: | else: | ||||
| fw('Ka %.6f %.6f %.6f\n' % (mat.ambient, mat.ambient, mat.ambient)) # Do not use world color! | fw('Ka %.6f %.6f %.6f\n' % (1.0, 1.0, 1.0)) | ||||
| fw('Kd %.6f %.6f %.6f\n' % (mat.diffuse_intensity * mat.diffuse_color)[:]) # Diffuse | fw('Kd %.6f %.6f %.6f\n' % mat_wrap.diffuse_color[:3]) # Diffuse | ||||
| fw('Ks %.6f %.6f %.6f\n' % (mat.specular_intensity * mat.specular_color)[:]) # Specular | # XXX TODO Find a way to handle tint and diffuse color, in a consistent way with import... | ||||
| fw('Ks %.6f %.6f %.6f\n' % (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular)) # Specular | |||||
| # Emission, not in original MTL standard but seems pretty common, see T45766. | # Emission, not in original MTL standard but seems pretty common, see T45766. | ||||
| # XXX Blender has no color emission, it's using diffuse color instead... | # XXX Not supported by current Principled-based shader. | ||||
| fw('Ke %.6f %.6f %.6f\n' % (mat.emit * mat.diffuse_color)[:]) | fw('Ke 0.0 0.0 0.0\n') | ||||
| if hasattr(mat, "raytrace_transparency") and hasattr(mat.raytrace_transparency, "ior"): | fw('Ni %.6f\n' % mat_wrap.ior) # Refraction index | ||||
| fw('Ni %.6f\n' % mat.raytrace_transparency.ior) # Refraction index | fw('d %.6f\n' % (1.0 - mat_wrap.transmission)) # Alpha (obj uses 'd' for dissolve) | ||||
| else: | |||||
| fw('Ni %.6f\n' % 1.0) | |||||
| fw('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) | |||||
| # See http://en.wikipedia.org/wiki/Wavefront_.obj_file for whole list of values... | # See http://en.wikipedia.org/wiki/Wavefront_.obj_file for whole list of values... | ||||
| # Note that mapping is rather fuzzy sometimes, trying to do our best here. | # Note that mapping is rather fuzzy sometimes, trying to do our best here. | ||||
| if mat.use_shadeless: | if mat_wrap.specular == 0: | ||||
| fw('illum 0\n') # ignore lighting | |||||
| elif mat.specular_intensity == 0: | |||||
| fw('illum 1\n') # no specular. | fw('illum 1\n') # no specular. | ||||
| elif use_mirror: | elif use_mirror: | ||||
| if mat.use_transparency and mat.transparency_method == 'RAYTRACE': | if use_transparency: | ||||
| if mat.raytrace_mirror.fresnel != 0.0: | |||||
| fw('illum 7\n') # Reflection, Transparency, Ray trace and Fresnel | |||||
| else: | |||||
| fw('illum 6\n') # Reflection, Transparency, Ray trace | fw('illum 6\n') # Reflection, Transparency, Ray trace | ||||
| elif mat.raytrace_mirror.fresnel != 0.0: | |||||
| fw('illum 5\n') # Reflection, Ray trace and Fresnel | |||||
| else: | else: | ||||
| fw('illum 3\n') # Reflection and Ray trace | fw('illum 3\n') # Reflection and Ray trace | ||||
| elif mat.use_transparency and mat.transparency_method == 'RAYTRACE': | elif use_transparency: | ||||
| fw('illum 9\n') # 'Glass' transparency and no Ray trace reflection... fuzzy matching, but... | fw('illum 9\n') # 'Glass' transparency and no Ray trace reflection... fuzzy matching, but... | ||||
| else: | else: | ||||
| fw('illum 2\n') # light normaly | fw('illum 2\n') # light normaly | ||||
| else: | |||||
| # Write a dummy material here? | |||||
| fw('Ns 0\n') | |||||
| fw('Ka %.6f %.6f %.6f\n' % world_amb[:]) # Ambient, uses mirror color, | |||||
| fw('Kd 0.8 0.8 0.8\n') | |||||
| fw('Ks 0.8 0.8 0.8\n') | |||||
| fw('d 1\n') # No alpha | |||||
| fw('illum 2\n') # light normaly | |||||
| # Write images! | #### And now, the image textures... | ||||
| if face_img: # We have an image on the face! | image_map = { | ||||
| filepath = face_img.filepath | "map_Kd": "diffuse_texture", | ||||
| if filepath: # may be '' for generated images | "map_Ka": None, # ambient... | ||||
| # write relative image path | "map_Ks": "specular_texture", | ||||
| filepath = bpy_extras.io_utils.path_reference(filepath, source_dir, dest_dir, | "map_Ns": "roughness_texture", | ||||
| path_mode, "", copy_set, face_img.library) | "map_d": "transmission_texture", | ||||
| fw('map_Kd %s\n' % filepath) # Diffuse mapping image | "map_Tr": None, # transmission roughness? | ||||
| del filepath | "map_Bump": "normalmap_texture", | ||||
| else: | "disp": None, # displacement... | ||||
| # so we write the materials image. | "refl": "metallic_texture", | ||||
| face_img = None | "map_Ke": None # emission... | ||||
| } | |||||
| if mat: # No face image. if we havea material search for MTex image. | |||||
| image_map = {} | for key, mat_wrap_key in sorted(image_map.items()): | ||||
| # backwards so topmost are highest priority | if mat_wrap_key is None: | ||||
| for mtex in reversed(mat.texture_slots): | continue | ||||
| if mtex and mtex.texture and mtex.texture.type == 'IMAGE': | tex_wrap = getattr(mat_wrap, mat_wrap_key, None) | ||||
| image = mtex.texture.image | if tex_wrap is None: | ||||
| if image: | continue | ||||
| # texface overrides others | image = tex_wrap.image | ||||
| if (mtex.use_map_color_diffuse and (face_img is None) and | if image is None: | ||||
| (mtex.use_map_warp is False) and (mtex.texture_coords != 'REFLECTION')): | continue | ||||
| image_map["map_Kd"] = (mtex, image) | |||||
| if mtex.use_map_ambient: | |||||
| image_map["map_Ka"] = (mtex, image) | |||||
| # this is the Spec intensity channel but Ks stands for specular Color | |||||
| ''' | |||||
| if mtex.use_map_specular: | |||||
| image_map["map_Ks"] = (mtex, image) | |||||
| ''' | |||||
| if mtex.use_map_color_spec: # specular color | |||||
| image_map["map_Ks"] = (mtex, image) | |||||
| if mtex.use_map_hardness: # specular hardness/glossiness | |||||
| image_map["map_Ns"] = (mtex, image) | |||||
| if mtex.use_map_alpha: | |||||
| image_map["map_d"] = (mtex, image) | |||||
| if mtex.use_map_translucency: | |||||
| image_map["map_Tr"] = (mtex, image) | |||||
| if mtex.use_map_normal: | |||||
| image_map["map_Bump"] = (mtex, image) | |||||
| if mtex.use_map_displacement: | |||||
| image_map["disp"] = (mtex, image) | |||||
| if mtex.use_map_color_diffuse and (mtex.texture_coords == 'REFLECTION'): | |||||
| image_map["refl"] = (mtex, image) | |||||
| if mtex.use_map_emit: | |||||
| image_map["map_Ke"] = (mtex, image) | |||||
| for key, (mtex, image) in sorted(image_map.items()): | filepath = io_utils.path_reference(image.filepath, source_dir, dest_dir, | ||||
| filepath = bpy_extras.io_utils.path_reference(image.filepath, source_dir, dest_dir, | |||||
| path_mode, "", copy_set, image.library) | path_mode, "", copy_set, image.library) | ||||
| options = [] | options = [] | ||||
| if key == "map_Bump": | if key == "map_Bump": | ||||
| if mtex.normal_factor != 1.0: | if mat_wrap.normalmap_strengh != 1.0: | ||||
| options.append('-bm %.6f' % mtex.normal_factor) | options.append('-bm %.6f' % mat_wrap.normalmap_strengh) | ||||
| if mtex.offset != Vector((0.0, 0.0, 0.0)): | if tex_wrap.translation != Vector((0.0, 0.0, 0.0)): | ||||
| options.append('-o %.6f %.6f %.6f' % mtex.offset[:]) | options.append('-o %.6f %.6f %.6f' % tex_wrap.translation[:]) | ||||
| if mtex.scale != Vector((1.0, 1.0, 1.0)): | if tex_wrap.scale != Vector((1.0, 1.0, 1.0)): | ||||
| options.append('-s %.6f %.6f %.6f' % mtex.scale[:]) | options.append('-s %.6f %.6f %.6f' % tex_wrap.scale[:]) | ||||
| if options: | if options: | ||||
| fw('%s %s %s\n' % (key, " ".join(options), repr(filepath)[1:-1])) | fw('%s %s %s\n' % (key, " ".join(options), repr(filepath)[1:-1])) | ||||
| else: | else: | ||||
| fw('%s %s\n' % (key, repr(filepath)[1:-1])) | fw('%s %s\n' % (key, repr(filepath)[1:-1])) | ||||
| else: | |||||
| # Write a dummy material here? | |||||
| fw('Ns 500\n') | |||||
| fw('Ka 0.8 0.8 0.8\n') | |||||
| fw('Kd 0.8 0.8 0.8\n') | |||||
| fw('Ks 0.8 0.8 0.8\n') | |||||
| fw('d 1\n') # No alpha | |||||
| fw('illum 2\n') # light normaly | |||||
| def test_nurbs_compat(ob): | def test_nurbs_compat(ob): | ||||
| if ob.type != 'CURVE': | if ob.type != 'CURVE': | ||||
| return False | return False | ||||
| for nu in ob.data.splines: | for nu in ob.data.splines: | ||||
| if nu.point_count_v == 1 and nu.type != 'BEZIER': # not a surface and not bezier | if nu.point_count_v == 1 and nu.type != 'BEZIER': # not a surface and not bezier | ||||
| return True | return True | ||||
| Show All 24 Lines | for nu in cu.splines: | ||||
| print("\tWarning, order_u is lower then vert count, skipping:", ob.name) | print("\tWarning, order_u is lower then vert count, skipping:", ob.name) | ||||
| continue | continue | ||||
| pt_num = 0 | pt_num = 0 | ||||
| do_closed = nu.use_cyclic_u | do_closed = nu.use_cyclic_u | ||||
| do_endpoints = (do_closed == 0) and nu.use_endpoint_u | do_endpoints = (do_closed == 0) and nu.use_endpoint_u | ||||
| for pt in nu.points: | for pt in nu.points: | ||||
| fw('v %.6f %.6f %.6f\n' % (ob_mat * pt.co.to_3d())[:]) | fw('v %.6f %.6f %.6f\n' % (ob_mat @ pt.co.to_3d())[:]) | ||||
| pt_num += 1 | pt_num += 1 | ||||
| tot_verts += pt_num | tot_verts += pt_num | ||||
| fw('g %s\n' % (name_compat(ob.name))) # name_compat(ob.getData(1)) could use the data name too | fw('g %s\n' % (name_compat(ob.name))) # name_compat(ob.getData(1)) could use the data name too | ||||
| fw('cstype bspline\n') # not ideal, hard coded | fw('cstype bspline\n') # not ideal, hard coded | ||||
| fw('deg %d\n' % DEG_ORDER_U) # not used for curves but most files have it still | fw('deg %d\n' % DEG_ORDER_U) # not used for curves but most files have it still | ||||
| curve_ls = [-(i + 1) for i in range(pt_num)] | curve_ls = [-(i + 1) for i in range(pt_num)] | ||||
| Show All 21 Lines | for nu in cu.splines: | ||||
| fw("parm u %s\n" % " ".join(["%.6f" % i for i in parm_ls])) | fw("parm u %s\n" % " ".join(["%.6f" % i for i in parm_ls])) | ||||
| fw('end\n') | fw('end\n') | ||||
| return tot_verts | return tot_verts | ||||
| def write_file(filepath, objects, scene, | def write_file(filepath, objects, depsgraph, scene, | ||||
| EXPORT_TRI=False, | EXPORT_TRI=False, | ||||
| EXPORT_EDGES=False, | EXPORT_EDGES=False, | ||||
| EXPORT_SMOOTH_GROUPS=False, | EXPORT_SMOOTH_GROUPS=False, | ||||
| EXPORT_SMOOTH_GROUPS_BITFLAGS=False, | EXPORT_SMOOTH_GROUPS_BITFLAGS=False, | ||||
| EXPORT_NORMALS=False, | EXPORT_NORMALS=False, | ||||
| EXPORT_UV=True, | EXPORT_UV=True, | ||||
| EXPORT_MTL=True, | EXPORT_MTL=True, | ||||
| EXPORT_APPLY_MODIFIERS=True, | EXPORT_APPLY_MODIFIERS=True, | ||||
| Show All 10 Lines | def write_file(filepath, objects, depsgraph, scene, | ||||
| ): | ): | ||||
| """ | """ | ||||
| Basic write function. The context and options must be already set | Basic write function. The context and options must be already set | ||||
| This can be accessed externaly | This can be accessed externaly | ||||
| eg. | eg. | ||||
| write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. | write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. | ||||
| """ | """ | ||||
| if EXPORT_GLOBAL_MATRIX is None: | if EXPORT_GLOBAL_MATRIX is None: | ||||
| EXPORT_GLOBAL_MATRIX = mathutils.Matrix() | EXPORT_GLOBAL_MATRIX = Matrix() | ||||
| def veckey3d(v): | def veckey3d(v): | ||||
| return round(v.x, 4), round(v.y, 4), round(v.z, 4) | return round(v.x, 4), round(v.y, 4), round(v.z, 4) | ||||
| def veckey2d(v): | def veckey2d(v): | ||||
| return round(v[0], 4), round(v[1], 4) | return round(v[0], 4), round(v[1], 4) | ||||
| def findVertexGroupName(face, vWeightMap): | def findVertexGroupName(face, vWeightMap): | ||||
| ▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | with ProgressReportSubstep(progress, 2, "OBJ Export path: %r" % filepath, "OBJ Export Finished") as subprogress1: | ||||
| subprogress1.enter_substeps(len(obs)) | subprogress1.enter_substeps(len(obs)) | ||||
| for ob, ob_mat in obs: | for ob, ob_mat in obs: | ||||
| with ProgressReportSubstep(subprogress1, 6) as subprogress2: | with ProgressReportSubstep(subprogress1, 6) as subprogress2: | ||||
| uv_unique_count = no_unique_count = 0 | uv_unique_count = no_unique_count = 0 | ||||
| # Nurbs curve support | # Nurbs curve support | ||||
| if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): | if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob): | ||||
| ob_mat = EXPORT_GLOBAL_MATRIX * ob_mat | ob_mat = EXPORT_GLOBAL_MATRIX @ ob_mat | ||||
| totverts += write_nurb(fw, ob, ob_mat) | totverts += write_nurb(fw, ob, ob_mat) | ||||
| continue | continue | ||||
| # END NURBS | # END NURBS | ||||
| try: | try: | ||||
| me = ob.to_mesh(scene, EXPORT_APPLY_MODIFIERS, calc_tessface=False, | me = ob.to_mesh(depsgraph, EXPORT_APPLY_MODIFIERS, calc_loop_triangles=False) | ||||
| settings='RENDER' if EXPORT_APPLY_MODIFIERS_RENDER else 'PREVIEW') | |||||
| except RuntimeError: | except RuntimeError: | ||||
| me = None | me = None | ||||
| if me is None: | if me is None: | ||||
| continue | continue | ||||
| # _must_ do this before applying transformation, else tessellation may differ | # _must_ do this before applying transformation, else tessellation may differ | ||||
| if EXPORT_TRI: | if EXPORT_TRI: | ||||
| # _must_ do this first since it re-allocs arrays | # _must_ do this first since it re-allocs arrays | ||||
| mesh_triangulate(me) | mesh_triangulate(me) | ||||
| me.transform(EXPORT_GLOBAL_MATRIX * ob_mat) | me.transform(EXPORT_GLOBAL_MATRIX @ ob_mat) | ||||
| # If negative scaling, we have to invert the normals... | # If negative scaling, we have to invert the normals... | ||||
| if ob_mat.determinant() < 0.0: | if ob_mat.determinant() < 0.0: | ||||
| me.flip_normals() | me.flip_normals() | ||||
| if EXPORT_UV: | if EXPORT_UV: | ||||
| faceuv = len(me.uv_textures) > 0 | faceuv = len(me.uv_layers) > 0 | ||||
| if faceuv: | if faceuv: | ||||
| uv_texture = me.uv_textures.active.data[:] | |||||
| uv_layer = me.uv_layers.active.data[:] | uv_layer = me.uv_layers.active.data[:] | ||||
| else: | else: | ||||
| faceuv = False | faceuv = False | ||||
| me_verts = me.vertices[:] | me_verts = me.vertices[:] | ||||
| # Make our own list so it can be sorted to reduce context switching | # Make our own list so it can be sorted to reduce context switching | ||||
| face_index_pairs = [(face, index) for index, face in enumerate(me.polygons)] | face_index_pairs = [(face, index) for index, face in enumerate(me.polygons)] | ||||
| # faces = [ f for f in me.tessfaces ] | |||||
| if EXPORT_EDGES: | if EXPORT_EDGES: | ||||
| edges = me.edges | edges = me.edges | ||||
| else: | else: | ||||
| edges = [] | edges = [] | ||||
| if not (len(face_index_pairs) + len(edges) + len(me.vertices)): # Make sure there is something to write | if not (len(face_index_pairs) + len(edges) + len(me.vertices)): # Make sure there is something to write | ||||
| # clean up | # clean up | ||||
| Show All 21 Lines | with ProgressReportSubstep(progress, 2, "OBJ Export path: %r" % filepath, "OBJ Export Finished") as subprogress1: | ||||
| materials = [None] | materials = [None] | ||||
| material_names = [name_compat(None)] | material_names = [name_compat(None)] | ||||
| # Sort by Material, then images | # Sort by Material, then images | ||||
| # so we dont over context switch in the obj file. | # so we dont over context switch in the obj file. | ||||
| if EXPORT_KEEP_VERT_ORDER: | if EXPORT_KEEP_VERT_ORDER: | ||||
| pass | pass | ||||
| else: | else: | ||||
| if faceuv: | if len(materials) > 1: | ||||
| if smooth_groups: | |||||
| sort_func = lambda a: (a[0].material_index, | |||||
| hash(uv_texture[a[1]].image), | |||||
| smooth_groups[a[1]] if a[0].use_smooth else False) | |||||
| else: | |||||
| sort_func = lambda a: (a[0].material_index, | |||||
| hash(uv_texture[a[1]].image), | |||||
| a[0].use_smooth) | |||||
| elif len(materials) > 1: | |||||
| if smooth_groups: | if smooth_groups: | ||||
| sort_func = lambda a: (a[0].material_index, | sort_func = lambda a: (a[0].material_index, | ||||
| smooth_groups[a[1]] if a[0].use_smooth else False) | smooth_groups[a[1]] if a[0].use_smooth else False) | ||||
| else: | else: | ||||
| sort_func = lambda a: (a[0].material_index, | sort_func = lambda a: (a[0].material_index, | ||||
| a[0].use_smooth) | a[0].use_smooth) | ||||
| else: | else: | ||||
| # no materials | # no materials | ||||
| ▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | with ProgressReportSubstep(progress, 2, "OBJ Export path: %r" % filepath, "OBJ Export Finished") as subprogress1: | ||||
| no_val = normals_to_idx[no_key] = no_unique_count | no_val = normals_to_idx[no_key] = no_unique_count | ||||
| fw('vn %.4f %.4f %.4f\n' % no_key) | fw('vn %.4f %.4f %.4f\n' % no_key) | ||||
| no_unique_count += 1 | no_unique_count += 1 | ||||
| loops_to_normals[l_idx] = no_val | loops_to_normals[l_idx] = no_val | ||||
| del normals_to_idx, no_get, no_key, no_val | del normals_to_idx, no_get, no_key, no_val | ||||
| else: | else: | ||||
| loops_to_normals = [] | loops_to_normals = [] | ||||
| if not faceuv: | |||||
| f_image = None | |||||
| subprogress2.step() | subprogress2.step() | ||||
| # XXX | # XXX | ||||
| if EXPORT_POLYGROUPS: | if EXPORT_POLYGROUPS: | ||||
| # Retrieve the list of vertex groups | # Retrieve the list of vertex groups | ||||
| vertGroupNames = ob.vertex_groups.keys() | vertGroupNames = ob.vertex_groups.keys() | ||||
| if vertGroupNames: | if vertGroupNames: | ||||
| currentVGroup = '' | currentVGroup = '' | ||||
| # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to | # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to | ||||
| vgroupsMap = [[] for _i in range(len(me_verts))] | vgroupsMap = [[] for _i in range(len(me_verts))] | ||||
| for v_idx, v_ls in enumerate(vgroupsMap): | for v_idx, v_ls in enumerate(vgroupsMap): | ||||
| v_ls[:] = [(vertGroupNames[g.group], g.weight) for g in me_verts[v_idx].groups] | v_ls[:] = [(vertGroupNames[g.group], g.weight) for g in me_verts[v_idx].groups] | ||||
| for f, f_index in face_index_pairs: | for f, f_index in face_index_pairs: | ||||
| f_smooth = f.use_smooth | f_smooth = f.use_smooth | ||||
| if f_smooth and smooth_groups: | if f_smooth and smooth_groups: | ||||
| f_smooth = smooth_groups[f_index] | f_smooth = smooth_groups[f_index] | ||||
| f_mat = min(f.material_index, len(materials) - 1) | f_mat = min(f.material_index, len(materials) - 1) | ||||
| if faceuv: | |||||
| tface = uv_texture[f_index] | |||||
| f_image = tface.image | |||||
| # MAKE KEY | # MAKE KEY | ||||
| if faceuv and f_image: # Object is always true. | |||||
| key = material_names[f_mat], f_image.name | |||||
| else: | |||||
| key = material_names[f_mat], None # No image, use None instead. | key = material_names[f_mat], None # No image, use None instead. | ||||
| # Write the vertex group | # Write the vertex group | ||||
| if EXPORT_POLYGROUPS: | if EXPORT_POLYGROUPS: | ||||
| if vertGroupNames: | if vertGroupNames: | ||||
| # find what vertext group the face belongs to | # find what vertext group the face belongs to | ||||
| vgroup_of_face = findVertexGroupName(f, vgroupsMap) | vgroup_of_face = findVertexGroupName(f, vgroupsMap) | ||||
| if vgroup_of_face != currentVGroup: | if vgroup_of_face != currentVGroup: | ||||
| currentVGroup = vgroup_of_face | currentVGroup = vgroup_of_face | ||||
| Show All 29 Lines | with ProgressReportSubstep(progress, 2, "OBJ Export path: %r" % filepath, "OBJ Export Finished") as subprogress1: | ||||
| tmp_ext = "_NONE" | tmp_ext = "_NONE" | ||||
| else: | else: | ||||
| tmp_ext = "_%s" % name_compat(key[1]) | tmp_ext = "_%s" % name_compat(key[1]) | ||||
| i = 0 | i = 0 | ||||
| while mtl_rev_dict.get(mtl_name + tmp_ext, None) not in {key, None}: | while mtl_rev_dict.get(mtl_name + tmp_ext, None) not in {key, None}: | ||||
| i += 1 | i += 1 | ||||
| tmp_ext = "_%3d" % i | tmp_ext = "_%3d" % i | ||||
| mtl_name += tmp_ext | mtl_name += tmp_ext | ||||
| mat_data = mtl_dict[key] = mtl_name, materials[f_mat], f_image | mat_data = mtl_dict[key] = mtl_name, materials[f_mat] | ||||
| mtl_rev_dict[mtl_name] = key | mtl_rev_dict[mtl_name] = key | ||||
| if EXPORT_GROUP_BY_MAT: | if EXPORT_GROUP_BY_MAT: | ||||
| # can be mat_image or (null) | # can be mat_image or (null) | ||||
| fw("g %s_%s_%s\n" % (name_compat(ob.name), name_compat(ob.data.name), mat_data[0])) | fw("g %s_%s_%s\n" % (name_compat(ob.name), name_compat(ob.data.name), mat_data[0])) | ||||
| if EXPORT_MTL: | if EXPORT_MTL: | ||||
| fw("usemtl %s\n" % mat_data[0]) # can be mat_image or (null) | fw("usemtl %s\n" % mat_data[0]) # can be mat_image or (null) | ||||
| ▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | with ProgressReportSubstep(progress, 2, "OBJ Export path: %r" % filepath, "OBJ Export Finished") as subprogress1: | ||||
| subprogress1.step("Finished exporting geometry, now exporting materials") | subprogress1.step("Finished exporting geometry, now exporting materials") | ||||
| # Now we have all our materials, save them | # Now we have all our materials, save them | ||||
| if EXPORT_MTL: | if EXPORT_MTL: | ||||
| write_mtl(scene, mtlfilepath, EXPORT_PATH_MODE, copy_set, mtl_dict) | write_mtl(scene, mtlfilepath, EXPORT_PATH_MODE, copy_set, mtl_dict) | ||||
| # copy all collected files. | # copy all collected files. | ||||
| bpy_extras.io_utils.path_reference_copy(copy_set) | io_utils.path_reference_copy(copy_set) | ||||
| def _write(context, filepath, | def _write(context, filepath, | ||||
| EXPORT_TRI, # ok | EXPORT_TRI, # ok | ||||
| EXPORT_EDGES, | EXPORT_EDGES, | ||||
| EXPORT_SMOOTH_GROUPS, | EXPORT_SMOOTH_GROUPS, | ||||
| EXPORT_SMOOTH_GROUPS_BITFLAGS, | EXPORT_SMOOTH_GROUPS_BITFLAGS, | ||||
| EXPORT_NORMALS, # ok | EXPORT_NORMALS, # ok | ||||
| Show All 12 Lines | def _write(context, filepath, | ||||
| EXPORT_GLOBAL_MATRIX, | EXPORT_GLOBAL_MATRIX, | ||||
| EXPORT_PATH_MODE, # Not used | EXPORT_PATH_MODE, # Not used | ||||
| ): | ): | ||||
| with ProgressReport(context.window_manager) as progress: | with ProgressReport(context.window_manager) as progress: | ||||
| base_name, ext = os.path.splitext(filepath) | base_name, ext = os.path.splitext(filepath) | ||||
| context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension | context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension | ||||
| depsgraph = context.depsgraph | |||||
| scene = context.scene | scene = context.scene | ||||
| # Exit edit mode before exporting, so current object states are exported properly. | # Exit edit mode before exporting, so current object states are exported properly. | ||||
| if bpy.ops.object.mode_set.poll(): | if bpy.ops.object.mode_set.poll(): | ||||
| bpy.ops.object.mode_set(mode='OBJECT') | bpy.ops.object.mode_set(mode='OBJECT') | ||||
| orig_frame = scene.frame_current | orig_frame = scene.frame_current | ||||
| # Export an animation? | # Export an animation? | ||||
| if EXPORT_ANIMATION: | if EXPORT_ANIMATION: | ||||
| scene_frames = range(scene.frame_start, scene.frame_end + 1) # Up to and including the end frame. | scene_frames = range(scene.frame_start, scene.frame_end + 1) # Up to and including the end frame. | ||||
| else: | else: | ||||
| scene_frames = [orig_frame] # Dont export an animation. | scene_frames = [orig_frame] # Dont export an animation. | ||||
| # Loop through all frames in the scene and export. | # Loop through all frames in the scene and export. | ||||
| progress.enter_substeps(len(scene_frames)) | progress.enter_substeps(len(scene_frames)) | ||||
| for frame in scene_frames: | for frame in scene_frames: | ||||
| if EXPORT_ANIMATION: # Add frame to the filepath. | if EXPORT_ANIMATION: # Add frame to the filepath. | ||||
| context_name[2] = '_%.6d' % frame | context_name[2] = '_%.6d' % frame | ||||
| scene.frame_set(frame, 0.0) | scene.frame_set(frame, subframe=0.0) | ||||
| if EXPORT_SEL_ONLY: | if EXPORT_SEL_ONLY: | ||||
| objects = context.selected_objects | objects = context.selected_objects | ||||
| else: | else: | ||||
| objects = scene.objects | objects = scene.objects | ||||
| full_path = ''.join(context_name) | full_path = ''.join(context_name) | ||||
| # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. | # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. | ||||
| # EXPORT THE FILE. | # EXPORT THE FILE. | ||||
| progress.enter_substeps(1) | progress.enter_substeps(1) | ||||
| write_file(full_path, objects, scene, | write_file(full_path, objects, depsgraph, scene, | ||||
| EXPORT_TRI, | EXPORT_TRI, | ||||
| EXPORT_EDGES, | EXPORT_EDGES, | ||||
| EXPORT_SMOOTH_GROUPS, | EXPORT_SMOOTH_GROUPS, | ||||
| EXPORT_SMOOTH_GROUPS_BITFLAGS, | EXPORT_SMOOTH_GROUPS_BITFLAGS, | ||||
| EXPORT_NORMALS, | EXPORT_NORMALS, | ||||
| EXPORT_UV, | EXPORT_UV, | ||||
| EXPORT_MTL, | EXPORT_MTL, | ||||
| EXPORT_APPLY_MODIFIERS, | EXPORT_APPLY_MODIFIERS, | ||||
| EXPORT_APPLY_MODIFIERS_RENDER, | EXPORT_APPLY_MODIFIERS_RENDER, | ||||
| EXPORT_BLEN_OBS, | EXPORT_BLEN_OBS, | ||||
| EXPORT_GROUP_BY_OB, | EXPORT_GROUP_BY_OB, | ||||
| EXPORT_GROUP_BY_MAT, | EXPORT_GROUP_BY_MAT, | ||||
| EXPORT_KEEP_VERT_ORDER, | EXPORT_KEEP_VERT_ORDER, | ||||
| EXPORT_POLYGROUPS, | EXPORT_POLYGROUPS, | ||||
| EXPORT_CURVE_AS_NURBS, | EXPORT_CURVE_AS_NURBS, | ||||
| EXPORT_GLOBAL_MATRIX, | EXPORT_GLOBAL_MATRIX, | ||||
| EXPORT_PATH_MODE, | EXPORT_PATH_MODE, | ||||
| progress, | progress, | ||||
| ) | ) | ||||
| progress.leave_substeps() | progress.leave_substeps() | ||||
| scene.frame_set(orig_frame, 0.0) | scene.frame_set(orig_frame, subframe=0.0) | ||||
| progress.leave_substeps() | progress.leave_substeps() | ||||
| """ | """ | ||||
| Currently the exporter lacks these features: | Currently the exporter lacks these features: | ||||
| * multiple scene export (only active scene is written) | * multiple scene export (only active scene is written) | ||||
| * particles | * particles | ||||
| """ | """ | ||||
| ▲ Show 20 Lines • Show All 49 Lines • Show Last 20 Lines | |||||