diff options
Diffstat (limited to 'io_scene_obj/export_obj.py')
-rw-r--r-- | io_scene_obj/export_obj.py | 231 |
1 files changed, 86 insertions, 145 deletions
diff --git a/io_scene_obj/export_obj.py b/io_scene_obj/export_obj.py index 05ff4db2..0e905141 100644 --- a/io_scene_obj/export_obj.py +++ b/io_scene_obj/export_obj.py @@ -21,8 +21,8 @@ import os import bpy -import mathutils -import bpy_extras.io_utils +from mathutils import Matrix, Vector, Color +from bpy_extras import io_utils, node_shader_utils from bpy_extras.wm_utils.progress_report import ( ProgressReport, @@ -47,13 +47,8 @@ def mesh_triangulate(me): def write_mtl(scene, filepath, path_mode, copy_set, mtl_dict): - from mathutils import Color, Vector - world = scene.world - if world: - world_amb = world.ambient_color - else: - world_amb = Color((0.0, 0.0, 0.0)) + world_amb = Color((0.8, 0.8, 0.8)) source_dir = os.path.dirname(bpy.data.filepath) dest_dir = os.path.dirname(filepath) @@ -69,133 +64,100 @@ def write_mtl(scene, filepath, path_mode, copy_set, mtl_dict): # Write material/image combinations we have used. # 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. # 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 - if mat: - use_mirror = mat.raytrace_mirror.use and mat.raytrace_mirror.reflect_factor != 0.0 + mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat) if mat else None - # convert from blenders spec to 0 - 1000 range. - if mat.specular_shader == 'WARDISO': - tspec = (0.4 - mat.specular_slope) / 0.0004 - else: - tspec = (mat.specular_hardness - 1) / 0.51 - fw('Ns %.6f\n' % tspec) - del tspec + if mat_wrap: + use_mirror = mat_wrap.metallic != 0.0 + use_transparency = mat_wrap.transmission != 0.0 + + # XXX Totally empirical conversion, trying to adapt it + # (from 1.0 - 0.0 Principled BSDF range to 0.0 - 900.0 OBJ specular exponent range)... + spec = (1.0 - mat_wrap.roughness) * 30 + spec *= spec + fw('Ns %.6f\n' % spec) # Ambient 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: - fw('Ka %.6f %.6f %.6f\n' % (mat.ambient, mat.ambient, mat.ambient)) # Do not use world color! - fw('Kd %.6f %.6f %.6f\n' % (mat.diffuse_intensity * mat.diffuse_color)[:]) # Diffuse - fw('Ks %.6f %.6f %.6f\n' % (mat.specular_intensity * mat.specular_color)[:]) # Specular + fw('Ka %.6f %.6f %.6f\n' % (1.0, 1.0, 1.0)) + fw('Kd %.6f %.6f %.6f\n' % mat_wrap.base_color[:3]) # Diffuse + # 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. - # XXX Blender has no color emission, it's using diffuse color instead... - fw('Ke %.6f %.6f %.6f\n' % (mat.emit * mat.diffuse_color)[:]) - if hasattr(mat, "raytrace_transparency") and hasattr(mat.raytrace_transparency, "ior"): - fw('Ni %.6f\n' % mat.raytrace_transparency.ior) # Refraction index - else: - fw('Ni %.6f\n' % 1.0) - fw('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) + # XXX Not supported by current Principled-based shader. + fw('Ke 0.0 0.0 0.0\n') + fw('Ni %.6f\n' % mat_wrap.ior) # Refraction index + fw('d %.6f\n' % (1.0 - mat_wrap.transmission)) # Alpha (obj uses 'd' for dissolve) # 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. - if mat.use_shadeless: - fw('illum 0\n') # ignore lighting - elif mat.specular_intensity == 0: + if mat_wrap.specular == 0: fw('illum 1\n') # no specular. elif use_mirror: - if mat.use_transparency and mat.transparency_method == 'RAYTRACE': - 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 - elif mat.raytrace_mirror.fresnel != 0.0: - fw('illum 5\n') # Reflection, Ray trace and Fresnel + if use_transparency: + fw('illum 6\n') # Reflection, Transparency, Ray trace else: 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... else: fw('illum 2\n') # light normally - 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 normally - - # Write images! - if face_img: # We have an image on the face! - filepath = face_img.filepath - if filepath: # may be '' for generated images - # write relative image path - filepath = bpy_extras.io_utils.path_reference(filepath, source_dir, dest_dir, - path_mode, "", copy_set, face_img.library) - fw('map_Kd %s\n' % filepath) # Diffuse mapping image - del filepath - else: - # so we write the materials image. - face_img = None - - if mat: # No face image. if we havea material search for MTex image. - image_map = {} - # backwards so topmost are highest priority - for mtex in reversed(mat.texture_slots): - if mtex and mtex.texture and mtex.texture.type == 'IMAGE': - image = mtex.texture.image - if image: - # texface overrides others - if (mtex.use_map_color_diffuse and (face_img is None) and - (mtex.use_map_warp is False) and (mtex.texture_coords != 'REFLECTION')): - 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 = bpy_extras.io_utils.path_reference(image.filepath, source_dir, dest_dir, - path_mode, "", copy_set, image.library) + #### And now, the image textures... + image_map = { + "map_Kd": "base_color_texture", + "map_Ka": None, # ambient... + "map_Ks": "specular_texture", + "map_Ns": "roughness_texture", + "map_d": "transmission_texture", + "map_Tr": None, # transmission roughness? + "map_Bump": "normalmap_texture", + "disp": None, # displacement... + "refl": "metallic_texture", + "map_Ke": None # emission... + } + + for key, mat_wrap_key in sorted(image_map.items()): + if mat_wrap_key is None: + continue + tex_wrap = getattr(mat_wrap, mat_wrap_key, None) + if tex_wrap is None: + continue + image = tex_wrap.image + if image is None: + continue + + filepath = io_utils.path_reference(image.filepath, source_dir, dest_dir, + path_mode, "", copy_set, image.library) options = [] if key == "map_Bump": - if mtex.normal_factor != 1.0: - options.append('-bm %.6f' % mtex.normal_factor) - if mtex.offset != Vector((0.0, 0.0, 0.0)): - options.append('-o %.6f %.6f %.6f' % mtex.offset[:]) - if mtex.scale != Vector((1.0, 1.0, 1.0)): - options.append('-s %.6f %.6f %.6f' % mtex.scale[:]) + if mat_wrap.normalmap_strengh != 1.0: + options.append('-bm %.6f' % mat_wrap.normalmap_strengh) + if tex_wrap.translation != Vector((0.0, 0.0, 0.0)): + options.append('-o %.6f %.6f %.6f' % tex_wrap.translation[:]) + if tex_wrap.scale != Vector((1.0, 1.0, 1.0)): + options.append('-s %.6f %.6f %.6f' % tex_wrap.scale[:]) if options: fw('%s %s %s\n' % (key, " ".join(options), repr(filepath)[1:-1])) else: 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 normally + def test_nurbs_compat(ob): if ob.type != 'CURVE': @@ -236,7 +198,7 @@ def write_nurb(fw, ob, ob_mat): do_endpoints = (do_closed == 0) and nu.use_endpoint_u 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 tot_verts += pt_num @@ -274,7 +236,7 @@ def write_nurb(fw, ob, ob_mat): return tot_verts -def write_file(filepath, objects, scene, +def write_file(filepath, objects, depsgraph, scene, EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_SMOOTH_GROUPS=False, @@ -301,7 +263,7 @@ def write_file(filepath, objects, scene, write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. """ if EXPORT_GLOBAL_MATRIX is None: - EXPORT_GLOBAL_MATRIX = mathutils.Matrix() + EXPORT_GLOBAL_MATRIX = Matrix() def veckey3d(v): return round(v.x, 4), round(v.y, 4), round(v.z, 4) @@ -362,13 +324,13 @@ def write_file(filepath, objects, scene, subprogress1.enter_substeps(len(objects)) for i, ob_main in enumerate(objects): # ignore dupli children - if ob_main.parent and ob_main.parent.dupli_type in {'VERTS', 'FACES'}: + if ob_main.parent and ob_main.parent.instance_type in {'VERTS', 'FACES'}: # XXX subprogress1.step("Ignoring %s, dupli child..." % ob_main.name) continue obs = [(ob_main, ob_main.matrix_world)] - if ob_main.dupli_type != 'NONE': + if ob_main.instance_type != 'NONE': # XXX print('creating dupli_list on', ob_main.name) ob_main.dupli_list_create(scene) @@ -385,14 +347,13 @@ def write_file(filepath, objects, scene, # Nurbs curve support 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) continue # END NURBS try: - me = ob.to_mesh(scene, EXPORT_APPLY_MODIFIERS, calc_tessface=False, - settings='RENDER' if EXPORT_APPLY_MODIFIERS_RENDER else 'PREVIEW') + me = ob.to_mesh(depsgraph, EXPORT_APPLY_MODIFIERS) except RuntimeError: me = None @@ -404,15 +365,14 @@ def write_file(filepath, objects, scene, # _must_ do this first since it re-allocs arrays 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 ob_mat.determinant() < 0.0: me.flip_normals() if EXPORT_UV: - faceuv = len(me.uv_textures) > 0 + faceuv = len(me.uv_layers) > 0 if faceuv: - uv_texture = me.uv_textures.active.data[:] uv_layer = me.uv_layers.active.data[:] else: faceuv = False @@ -421,7 +381,6 @@ def write_file(filepath, objects, scene, # 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)] - # faces = [ f for f in me.tessfaces ] if EXPORT_EDGES: edges = me.edges @@ -459,16 +418,7 @@ def write_file(filepath, objects, scene, if EXPORT_KEEP_VERT_ORDER: pass else: - if faceuv: - 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 len(materials) > 1: if smooth_groups: sort_func = lambda a: (a[0].material_index, smooth_groups[a[1]] if a[0].use_smooth else False) @@ -562,9 +512,6 @@ def write_file(filepath, objects, scene, else: loops_to_normals = [] - if not faceuv: - f_image = None - subprogress2.step() # XXX @@ -584,15 +531,8 @@ def write_file(filepath, objects, scene, f_smooth = smooth_groups[f_index] f_mat = min(f.material_index, len(materials) - 1) - if faceuv: - tface = uv_texture[f_index] - f_image = tface.image - # 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 if EXPORT_POLYGROUPS: @@ -638,7 +578,7 @@ def write_file(filepath, objects, scene, i += 1 tmp_ext = "_%3d" % i 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 if EXPORT_GROUP_BY_MAT: @@ -704,7 +644,7 @@ def write_file(filepath, objects, scene, # clean up bpy.data.meshes.remove(me) - if ob_main.dupli_type != 'NONE': + if ob_main.instance_type != 'NONE': ob_main.dupli_list_clear() subprogress1.leave_substeps("Finished writing geometry of '%s'." % ob_main.name) @@ -717,7 +657,7 @@ def write_file(filepath, objects, scene, write_mtl(scene, mtlfilepath, EXPORT_PATH_MODE, copy_set, mtl_dict) # copy all collected files. - bpy_extras.io_utils.path_reference_copy(copy_set) + io_utils.path_reference_copy(copy_set) def _write(context, filepath, @@ -746,6 +686,7 @@ def _write(context, filepath, base_name, ext = os.path.splitext(filepath) context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension + depsgraph = context.depsgraph scene = context.scene # Exit edit mode before exporting, so current object states are exported properly. @@ -766,7 +707,7 @@ def _write(context, filepath, if EXPORT_ANIMATION: # Add frame to the filepath. context_name[2] = '_%.6d' % frame - scene.frame_set(frame, 0.0) + scene.frame_set(frame, subframe=0.0) if EXPORT_SEL_ONLY: objects = context.selected_objects else: @@ -777,7 +718,7 @@ def _write(context, filepath, # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. # EXPORT THE FILE. progress.enter_substeps(1) - write_file(full_path, objects, scene, + write_file(full_path, objects, depsgraph, scene, EXPORT_TRI, EXPORT_EDGES, EXPORT_SMOOTH_GROUPS, @@ -799,7 +740,7 @@ def _write(context, filepath, ) progress.leave_substeps() - scene.frame_set(orig_frame, 0.0) + scene.frame_set(orig_frame, subframe=0.0) progress.leave_substeps() |