Changeset View
Changeset View
Standalone View
Standalone View
io_scene_obj/import_obj.py
| Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | if image is None: | ||||
| imagepath = os.fsdecode(filepath_parts[-1]) | imagepath = os.fsdecode(filepath_parts[-1]) | ||||
| image = load_image(imagepath, DIR, recursive=recursive, place_holder=True, relpath=relpath) | image = load_image(imagepath, DIR, recursive=recursive, place_holder=True, relpath=relpath) | ||||
| context_imagepath_map[imagepath] = image | context_imagepath_map[imagepath] = image | ||||
| return image | return image | ||||
| def create_materials(filepath, relpath, | def create_materials(filepath, relpath, | ||||
| material_libs, unique_materials, unique_material_images, | material_libs, unique_materials, | ||||
| use_image_search, use_cycles, float_func): | use_image_search, float_func): | ||||
| """ | """ | ||||
| Create all the used materials in this obj, | Create all the used materials in this obj, | ||||
| assign colors and images to the materials from all referenced material libs | assign colors and images to the materials from all referenced material libs | ||||
| """ | """ | ||||
| DIR = os.path.dirname(filepath) | DIR = os.path.dirname(filepath) | ||||
| context_material_vars = set() | context_material_vars = set() | ||||
| # Don't load the same image multiple times | # Don't load the same image multiple times | ||||
| context_imagepath_map = {} | context_imagepath_map = {} | ||||
| cycles_material_wrap_map = {} | nodal_material_wrap_map = {} | ||||
| def load_material_image(blender_material, mat_wrap, use_cycles, context_material_name, img_data, line, type): | def load_material_image(blender_material, mat_wrap, context_material_name, img_data, line, type): | ||||
| """ | """ | ||||
| Set textures defined in .mtl file. | Set textures defined in .mtl file. | ||||
| """ | """ | ||||
| map_options = {} | map_options = {} | ||||
| curr_token = [] | curr_token = [] | ||||
| for token in img_data[:-1]: | for token in img_data[:-1]: | ||||
| if token.startswith(b'-') and token[1:].isalpha(): | if token.startswith(b'-') and token[1:].isalpha(): | ||||
| if curr_token: | if curr_token: | ||||
| map_options[curr_token[0]] = curr_token[1:] | map_options[curr_token[0]] = curr_token[1:] | ||||
| curr_token[:] = [] | curr_token[:] = [] | ||||
| curr_token.append(token) | curr_token.append(token) | ||||
| if curr_token: | if curr_token: | ||||
| map_options[curr_token[0]] = curr_token[1:] | map_options[curr_token[0]] = curr_token[1:] | ||||
| # Absolute path - c:\.. etc would work here | # Absolute path - c:\.. etc would work here | ||||
| image = obj_image_load(context_imagepath_map, line, DIR, use_image_search, relpath) | image = obj_image_load(context_imagepath_map, line, DIR, use_image_search, relpath) | ||||
| texture = bpy.data.textures.new(name=type, type='IMAGE') | |||||
| if image is not None: | |||||
| texture.image = image | |||||
| map_offset = map_options.get(b'-o') | map_offset = map_options.get(b'-o') | ||||
| map_scale = map_options.get(b'-s') | map_scale = map_options.get(b'-s') | ||||
| def _generic_tex_set(nodetex, image, texcoords, translation, scale): | |||||
| nodetex.image = image | |||||
| nodetex.texcoords = texcoords | |||||
| if translation is not None: | |||||
| nodetex.translation = translation | |||||
| if scale is not None: | |||||
| nodetex.scale = scale | |||||
| # Adds textures for materials (rendering) | # Adds textures for materials (rendering) | ||||
| if type == 'Kd': | if type == 'Kd': | ||||
| if use_cycles: | _generic_tex_set(mat_wrap.diffuse_texture, image, 'UV', map_offset, map_scale) | ||||
| mat_wrap.diffuse_image_set(image) | |||||
| mat_wrap.diffuse_mapping_set(coords='UV', translation=map_offset, scale=map_scale) | |||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'UV' | |||||
| mtex.use_map_color_diffuse = True | |||||
| # 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 # set the texface image | |||||
| elif type == 'Ka': | elif type == 'Ka': | ||||
| if use_cycles: | |||||
| # XXX Not supported? | # XXX Not supported? | ||||
| print("WARNING, currently unsupported ambient texture, skipped.") | print("WARNING, currently unsupported ambient texture, skipped.") | ||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.use_map_color_diffuse = False | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'UV' | |||||
| mtex.use_map_ambient = True | |||||
| elif type == 'Ks': | elif type == 'Ks': | ||||
| if use_cycles: | _generic_tex_set(mat_wrap.specular_texture, image, 'UV', map_offset, map_scale) | ||||
| mat_wrap.specular_image_set(image) | |||||
| mat_wrap.specular_mapping_set(coords='UV', translation=map_offset, scale=map_scale) | |||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.use_map_color_diffuse = False | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'UV' | |||||
| mtex.use_map_color_spec = True | |||||
| elif type == 'Ke': | elif type == 'Ke': | ||||
| if use_cycles: | |||||
| # XXX Not supported? | # XXX Not supported? | ||||
| print("WARNING, currently unsupported emit texture, skipped.") | print("WARNING, currently unsupported emit texture, skipped.") | ||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.use_map_color_diffuse = False | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'UV' | |||||
| mtex.use_map_emit = True | |||||
| elif type == 'Bump': | elif type == 'Bump': | ||||
| bump_mult = map_options.get(b'-bm') | bump_mult = map_options.get(b'-bm') | ||||
| bump_mult = float(bump_mult[0]) if (bump_mult and len(bump_mult[0]) > 1) else 1.0 | bump_mult = float(bump_mult[0]) if (bump_mult and len(bump_mult[0]) > 1) else 1.0 | ||||
| mat_wrap.normalmap_strength_set(bump_mult) | |||||
| if use_cycles: | _generic_tex_set(mat_wrap.normalmap_texture, image, 'UV', map_offset, map_scale) | ||||
| mat_wrap.normal_image_set(image) | |||||
| mat_wrap.normal_mapping_set(coords='UV', translation=map_offset, scale=map_scale) | |||||
| if bump_mult: | |||||
| mat_wrap.normal_factor_set(bump_mult) | |||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.use_map_color_diffuse = False | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'UV' | |||||
| mtex.use_map_normal = True | |||||
| if bump_mult: | |||||
| mtex.normal_factor = bump_mult | |||||
| elif type == 'D': | elif type == 'D': | ||||
| if use_cycles: | _generic_tex_set(mat_wrap.transmission_texture, image, 'UV', map_offset, map_scale) | ||||
| mat_wrap.alpha_image_set(image) | |||||
| mat_wrap.alpha_mapping_set(coords='UV', translation=map_offset, scale=map_scale) | |||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.use_map_color_diffuse = False | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'UV' | |||||
| mtex.use_map_alpha = True | |||||
| blender_material.use_transparency = True | |||||
| blender_material.transparency_method = 'Z_TRANSPARENCY' | |||||
| if "alpha" not in context_material_vars: | |||||
| blender_material.alpha = 0.0 | |||||
| # Todo, unset diffuse material alpha if it has an alpha channel | |||||
| elif type == 'disp': | elif type == 'disp': | ||||
| if use_cycles: | # XXX Not supported? | ||||
| mat_wrap.bump_image_set(image) | print("WARNING, currently unsupported displacement texture, skipped.") | ||||
| mat_wrap.bump_mapping_set(coords='UV', translation=map_offset, scale=map_scale) | # ~ mat_wrap.bump_image_set(image) | ||||
| # ~ mat_wrap.bump_mapping_set(coords='UV', translation=map_offset, scale=map_scale) | |||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.use_map_color_diffuse = False | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'UV' | |||||
| mtex.use_map_displacement = True | |||||
| elif type == 'refl': | elif type == 'refl': | ||||
| map_type = map_options.get(b'-type') | map_type = map_options.get(b'-type') | ||||
| if map_type and map_type != [b'sphere']: | if map_type and map_type != [b'sphere']: | ||||
| print("WARNING, unsupported reflection type '%s', defaulting to 'sphere'" | print("WARNING, unsupported reflection type '%s', defaulting to 'sphere'" | ||||
| "" % ' '.join(i.decode() for i in map_type)) | "" % ' '.join(i.decode() for i in map_type)) | ||||
| if use_cycles: | _generic_tex_set(mat_wrap.diffuse_texture, image, 'Reflection', map_offset, map_scale) | ||||
| mat_wrap.diffuse_image_set(image, projection='SPHERE') | mat_wrap.diffuse_texture.projection = 'SPHERE' | ||||
| mat_wrap.diffuse_mapping_set(coords='Reflection', translation=map_offset, scale=map_scale) | |||||
| mtex = blender_material.texture_slots.add() | |||||
| mtex.use_map_color_diffuse = False | |||||
| mtex.texture = texture | |||||
| mtex.texture_coords = 'REFLECTION' | |||||
| mtex.use_map_color_diffuse = True | |||||
| mtex.mapping = 'SPHERE' | |||||
| else: | else: | ||||
| raise Exception("invalid type %r" % type) | raise Exception("invalid type %r" % type) | ||||
| if map_offset: | |||||
| mtex.offset.x = float(map_offset[0]) | |||||
| if len(map_offset) >= 2: | |||||
| mtex.offset.y = float(map_offset[1]) | |||||
| if len(map_offset) >= 3: | |||||
| mtex.offset.z = float(map_offset[2]) | |||||
| if map_scale: | |||||
| mtex.scale.x = float(map_scale[0]) | |||||
| if len(map_scale) >= 2: | |||||
| mtex.scale.y = float(map_scale[1]) | |||||
| if len(map_scale) >= 3: | |||||
| mtex.scale.z = float(map_scale[2]) | |||||
| # Add an MTL with the same name as the obj if no MTLs are spesified. | # Add an MTL with the same name as the obj if no MTLs are spesified. | ||||
| temp_mtl = os.path.splitext((os.path.basename(filepath)))[0] + ".mtl" | temp_mtl = os.path.splitext((os.path.basename(filepath)))[0] + ".mtl" | ||||
| if os.path.exists(os.path.join(DIR, temp_mtl)): | if os.path.exists(os.path.join(DIR, temp_mtl)): | ||||
| material_libs.add(temp_mtl) | material_libs.add(temp_mtl) | ||||
| del temp_mtl | del temp_mtl | ||||
| # Create new materials | # Create new materials | ||||
| for name in unique_materials: # .keys() | for name in unique_materials: # .keys() | ||||
| if name is not None: | if name is not None: | ||||
| ma = unique_materials[name] = bpy.data.materials.new(name.decode('utf-8', "replace")) | ma = unique_materials[name] = bpy.data.materials.new(name.decode('utf-8', "replace")) | ||||
| unique_material_images[name] = None # assign None to all material images to start with, add to later. | from bpy_extras import node_shader_utils | ||||
| if use_cycles: | ma_wrap = node_shader_utils.PrincipledBSDFWrapper(ma, is_readonly=False) | ||||
| from modules import cycles_shader_compat | nodal_material_wrap_map[ma] = ma_wrap | ||||
| ma_wrap = cycles_shader_compat.CyclesShaderWrapper(ma) | ma_wrap.use_nodes = True | ||||
| cycles_material_wrap_map[ma] = ma_wrap | |||||
| # XXX Why was this needed? Cannot find any good reason, and adds stupid empty matslot in case we do not separate | |||||
| # mesh (see T44947). | |||||
| #~ unique_materials[None] = None | |||||
| #~ unique_material_images[None] = None | |||||
| for libname in sorted(material_libs): | for libname in sorted(material_libs): | ||||
| # print(libname) | # print(libname) | ||||
| mtlpath = os.path.join(DIR, libname) | mtlpath = os.path.join(DIR, libname) | ||||
| if not os.path.exists(mtlpath): | if not os.path.exists(mtlpath): | ||||
| print("\tMaterial not found MTL: %r" % mtlpath) | print("\tMaterial not found MTL: %r" % mtlpath) | ||||
| else: | else: | ||||
| do_ambient = True | # Note: with modern Principled BSDF shader, things like ambient, raytrace or fresnel are always 'ON' | ||||
| # (i.e. automatically controlled by other parameters). | |||||
| do_highlight = False | do_highlight = False | ||||
| do_reflection = False | do_reflection = False | ||||
| do_transparency = False | do_transparency = False | ||||
| do_glass = False | do_glass = False | ||||
| do_fresnel = False | spec_colors = [0.0, 0.0, 0.0] | ||||
| do_raytrace = False | |||||
| emit_colors = [0.0, 0.0, 0.0] | emit_colors = [0.0, 0.0, 0.0] | ||||
| # print('\t\tloading mtl: %e' % mtlpath) | # print('\t\tloading mtl: %e' % mtlpath) | ||||
| context_material = None | context_material = None | ||||
| context_mat_wrap = None | context_mat_wrap = None | ||||
| mtl = open(mtlpath, 'rb') | mtl = open(mtlpath, 'rb') | ||||
| for line in mtl: # .readlines(): | for line in mtl: # .readlines(): | ||||
| line = line.strip() | line = line.strip() | ||||
| if not line or line.startswith(b'#'): | if not line or line.startswith(b'#'): | ||||
| continue | continue | ||||
| line_split = line.split() | line_split = line.split() | ||||
| line_id = line_split[0].lower() | line_id = line_split[0].lower() | ||||
| if line_id == b'newmtl': | if line_id == b'newmtl': | ||||
| # Finalize previous mat, if any. | # Finalize previous mat, if any. | ||||
| if context_material: | if context_material: | ||||
| if "specular" in context_material_vars: | |||||
| # XXX This is highly approximated, not sure whether we can do better... | |||||
| # TODO: Find a way to guesstimate best value from diffuse color... | |||||
| # IDEA: Use standard deviation of both spec and diff colors (i.e. how far away they are | |||||
| # from some grey), and apply the the proportion between those two as tint factor? | |||||
| # ~ spec = sum(spec_color) / 3.0 | |||||
| # ~ spec_var = math.sqrt(sum((c - spec) ** 2 for c in spec_color) / 3.0) | |||||
| # ~ diff = sum(context_mat_wrap.diffuse_color[:3]) / 3.0 | |||||
| # ~ diff_var = math.sqrt(sum((c - diff) ** 2 for c in context_mat_wrap.diffuse_color[:3]) / 3.0) | |||||
| # ~ tint = min(1.0, spec_var / diff_var) | |||||
| context_mat_wrap.specular = spec | |||||
| context_mat_wrap.specular_tint = 0.0 | |||||
| if "roughness" not in context_material_vars: | |||||
| context_mat_wrap.roughness = 0.0 | |||||
| emit_value = sum(emit_colors) / 3.0 | emit_value = sum(emit_colors) / 3.0 | ||||
| if emit_value > 1e-6: | if emit_value > 1e-6: | ||||
| if use_cycles: | print("WARNING, emit value unsupported by Principled BSDF shader, skipped.") | ||||
| print("WARNING, currently unsupported emit value, skipped.") | |||||
| # We have to adapt it to diffuse color too... | # We have to adapt it to diffuse color too... | ||||
| emit_value /= sum(context_material.diffuse_color) / 3.0 | emit_value /= sum(context_material.diffuse_color) / 3.0 | ||||
| context_material.emit = emit_value | # ~ context_material.emit = emit_value | ||||
| if not do_ambient: | |||||
| context_material.ambient = 0.0 | |||||
| if do_highlight: | |||||
| if use_cycles: | |||||
| context_mat_wrap.hardness_value_set(1.0) | |||||
| # FIXME, how else to use this? | # FIXME, how else to use this? | ||||
| context_material.specular_intensity = 1.0 | if do_highlight: | ||||
| if "specular" not in context_material_vars: | |||||
| context_mat_wrap.specular = 1.0 | |||||
| if "roughness" not in context_material_vars: | |||||
| context_mat_wrap.roughness = 0.0 | |||||
| else: | else: | ||||
| if use_cycles: | if "specular" not in context_material_vars: | ||||
| context_mat_wrap.hardness_value_set(0.0) | context_mat_wrap.specular = 0.0 | ||||
| if "roughness" not in context_material_vars: | |||||
| context_mat_wrap.roughness = 1.0 | |||||
| if do_reflection: | if do_reflection: | ||||
| if use_cycles: | if "metallic" not in context_material_vars: | ||||
| context_mat_wrap.reflect_factor_set(1.0) | context_mat_wrap.metallic = 1.0 | ||||
| context_material.raytrace_mirror.use = True | |||||
| context_material.raytrace_mirror.reflect_factor = 1.0 | |||||
| if do_transparency: | if do_transparency: | ||||
| context_material.use_transparency = True | if "ior" not in context_material_vars: | ||||
| context_material.transparency_method = 'RAYTRACE' if do_raytrace else 'Z_TRANSPARENCY' | context_mat_wrap.ior = 1.0 | ||||
| if "alpha" not in context_material_vars: | if "transmission" not in context_material_vars: | ||||
| if use_cycles: | context_mat_wrap.transmission = 1.0 | ||||
| context_mat_wrap.alpha_value_set(0.0) | # EEVEE only | ||||
| context_material.alpha = 0.0 | context_material.blend_method = 'BLEND' | ||||
| if do_glass: | if do_glass: | ||||
| if use_cycles: | |||||
| print("WARNING, currently unsupported glass material, skipped.") | |||||
| if "ior" not in context_material_vars: | if "ior" not in context_material_vars: | ||||
| context_material.raytrace_transparency.ior = 1.5 | context_mat_wrap.ior = 1.5 | ||||
| if do_fresnel: | |||||
| if use_cycles: | |||||
| print("WARNING, currently unsupported fresnel option, skipped.") | |||||
| context_material.raytrace_mirror.fresnel = 1.0 # could be any value for 'ON' | |||||
| """ | |||||
| if do_raytrace: | |||||
| context_material.use_raytrace = True | |||||
| else: | |||||
| context_material.use_raytrace = False | |||||
| """ | |||||
| # XXX, this is not following the OBJ spec, but this was | |||||
| # written when raytracing wasnt default, annoying to disable for blender users. | |||||
| context_material.use_raytrace = True | |||||
| context_material_name = line_value(line_split) | context_material_name = line_value(line_split) | ||||
| context_material = unique_materials.get(context_material_name) | context_material = unique_materials.get(context_material_name) | ||||
| if use_cycles and context_material is not None: | if context_material is not None: | ||||
| context_mat_wrap = cycles_material_wrap_map[context_material] | context_mat_wrap = nodal_material_wrap_map[context_material] | ||||
| context_material_vars.clear() | context_material_vars.clear() | ||||
| spec_colors = [0.0, 0.0, 0.0] | |||||
| emit_colors[:] = [0.0, 0.0, 0.0] | emit_colors[:] = [0.0, 0.0, 0.0] | ||||
| do_ambient = True | |||||
| do_highlight = False | do_highlight = False | ||||
| do_reflection = False | do_reflection = False | ||||
| do_transparency = False | do_transparency = False | ||||
| do_glass = False | do_glass = False | ||||
| do_fresnel = False | |||||
| do_raytrace = False | |||||
| elif context_material: | elif context_material: | ||||
| # we need to make a material to assign properties to it. | # we need to make a material to assign properties to it. | ||||
| if line_id == b'ka': | if line_id == b'ka': | ||||
| col = (float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3])) | refl = (float_func(line_split[1]) + float_func(line_split[2]) + float_func(line_split[3])) / 3.0 | ||||
| if use_cycles: | context_mat_wrap.metallic = refl | ||||
| context_mat_wrap.reflect_color_set(col) | context_material_vars.add("metallic") | ||||
| context_material.mirror_color = col | |||||
| # This is highly approximated, but let's try to stick as close from exporter as possible... :/ | |||||
| context_material.ambient = sum(context_material.mirror_color) / 3 | |||||
| elif line_id == b'kd': | elif line_id == b'kd': | ||||
| col = (float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3])) | col = (float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3])) | ||||
| if use_cycles: | context_mat_wrap.diffuse_color[:3] = col | ||||
| context_mat_wrap.diffuse_color_set(col) | |||||
| context_material.diffuse_color = col | |||||
| context_material.diffuse_intensity = 1.0 | |||||
| elif line_id == b'ks': | elif line_id == b'ks': | ||||
| col = (float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3])) | spec_color = (float_func(line_split[1]) + float_func(line_split[2]) + float_func(line_split[3])) | ||||
| if use_cycles: | context_material_vars.add("specular") | ||||
| context_mat_wrap.specular_color_set(col) | |||||
| context_mat_wrap.hardness_value_set(1.0) | |||||
| context_material.specular_color = col | |||||
| context_material.specular_intensity = 1.0 | |||||
| elif line_id == b'ke': | elif line_id == b'ke': | ||||
| # We cannot set context_material.emit right now, we need final diffuse color as well for this. | # We cannot set context_material.emit right now, we need final diffuse color as well for this. | ||||
| # XXX Unsuported currently | |||||
| emit_colors[:] = [ | emit_colors[:] = [ | ||||
| float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3])] | float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3])] | ||||
| elif line_id == b'ns': | elif line_id == b'ns': | ||||
| if use_cycles: | # XXX Basic linear conversion, what would be best-matching formula here? | ||||
| context_mat_wrap.hardness_value_set(((float_func(line_split[1]) + 3.0) / 50.0) - 0.65) | context_mat_wrap.roughness = 1.0 - (float_func(line_split[1]) / 1000) | ||||
| context_material.specular_hardness = int((float_func(line_split[1]) * 0.51) + 1) | context_material_vars.add("roughness") | ||||
| elif line_id == b'ni': # Refraction index (between 1 and 3). | elif line_id == b'ni': # Refraction index (between 0.001 and 10). | ||||
| if use_cycles: | context_mat_wrap.ior = float_func(line_split[1]) | ||||
| print("WARNING, currently unsupported glass material, skipped.") | |||||
| context_material.raytrace_transparency.ior = max(1, min(float_func(line_split[1]), 3)) | |||||
| context_material_vars.add("ior") | context_material_vars.add("ior") | ||||
| elif line_id == b'd': # dissolve (transparency) | elif line_id == b'd': # dissolve (transparency) | ||||
| if use_cycles: | context_mat_wrap.transmission = 1.0 - float_func(line_split[1]) | ||||
| context_mat_wrap.alpha_value_set(float_func(line_split[1])) | context_material_vars.add("transmission") | ||||
| context_material.alpha = float_func(line_split[1]) | |||||
| context_material.use_transparency = True | |||||
| context_material.transparency_method = 'Z_TRANSPARENCY' | |||||
| context_material_vars.add("alpha") | |||||
| elif line_id == b'tr': # translucency | elif line_id == b'tr': # translucency | ||||
| if use_cycles: | print("WARNING, currently unsupported 'tr' translucency option, skipped.") | ||||
| print("WARNING, currently unsupported translucency option, skipped.") | |||||
| context_material.translucency = float_func(line_split[1]) | |||||
| elif line_id == b'tf': | elif line_id == b'tf': | ||||
| # rgb, filter color, blender has no support for this. | # rgb, filter color, blender has no support for this. | ||||
| pass | print("WARNING, currently unsupported 'tf' filter color option, skipped.") | ||||
| elif line_id == b'illum': | elif line_id == b'illum': | ||||
| illum = int(line_split[1]) | illum = int(line_split[1]) | ||||
| # inline comments are from the spec, v4.2 | # inline comments are from the spec, v4.2 | ||||
| if illum == 0: | if illum == 0: | ||||
| # Color on and Ambient off | # Color on and Ambient off | ||||
| do_ambient = False | print("WARNING, Principled BSDF shader does not support illumination 0 mode " | ||||
| "(colors with no ambient), skipped.") | |||||
| elif illum == 1: | elif illum == 1: | ||||
| # Color on and Ambient on | # Color on and Ambient on | ||||
| pass | pass | ||||
| elif illum == 2: | elif illum == 2: | ||||
| # Highlight on | # Highlight on | ||||
| do_highlight = True | do_highlight = True | ||||
| elif illum == 3: | elif illum == 3: | ||||
| # Reflection on and Ray trace on | # Reflection on and Ray trace on | ||||
| do_reflection = True | do_reflection = True | ||||
| do_raytrace = True | |||||
| elif illum == 4: | elif illum == 4: | ||||
| # Transparency: Glass on | # Transparency: Glass on | ||||
| # Reflection: Ray trace on | # Reflection: Ray trace on | ||||
| do_transparency = True | do_transparency = True | ||||
| do_reflection = True | do_reflection = True | ||||
| do_glass = True | do_glass = True | ||||
| do_raytrace = True | |||||
| elif illum == 5: | elif illum == 5: | ||||
| # Reflection: Fresnel on and Ray trace on | # Reflection: Fresnel on and Ray trace on | ||||
| do_reflection = True | do_reflection = True | ||||
| do_fresnel = True | |||||
| do_raytrace = True | |||||
| elif illum == 6: | elif illum == 6: | ||||
| # Transparency: Refraction on | # Transparency: Refraction on | ||||
| # Reflection: Fresnel off and Ray trace on | # Reflection: Fresnel off and Ray trace on | ||||
| do_transparency = True | do_transparency = True | ||||
| do_reflection = True | do_reflection = True | ||||
| do_raytrace = True | |||||
| elif illum == 7: | elif illum == 7: | ||||
| # Transparency: Refraction on | # Transparency: Refraction on | ||||
| # Reflection: Fresnel on and Ray trace on | # Reflection: Fresnel on and Ray trace on | ||||
| do_transparency = True | do_transparency = True | ||||
| do_reflection = True | do_reflection = True | ||||
| do_fresnel = True | |||||
| do_raytrace = True | |||||
| elif illum == 8: | elif illum == 8: | ||||
| # Reflection on and Ray trace off | # Reflection on and Ray trace off | ||||
| do_reflection = True | do_reflection = True | ||||
| elif illum == 9: | elif illum == 9: | ||||
| # Transparency: Glass on | # Transparency: Glass on | ||||
| # Reflection: Ray trace off | # Reflection: Ray trace off | ||||
| do_transparency = True | do_transparency = True | ||||
| do_reflection = True | do_reflection = False | ||||
| do_glass = True | do_glass = True | ||||
| elif illum == 10: | elif illum == 10: | ||||
| # Casts shadows onto invisible surfaces | # Casts shadows onto invisible surfaces | ||||
| print("WARNING, Principled BSDF shader does not support illumination 10 mode " | |||||
| # blender can't do this | "(cast shadows on invisible surfaces), skipped.") | ||||
| pass | pass | ||||
| elif line_id == b'map_ka': | elif line_id == b'map_ka': | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'Ka') | context_material_name, img_data, line, 'Ka') | ||||
| elif line_id == b'map_ks': | elif line_id == b'map_ks': | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'Ks') | context_material_name, img_data, line, 'Ks') | ||||
| elif line_id == b'map_kd': | elif line_id == b'map_kd': | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'Kd') | context_material_name, img_data, line, 'Kd') | ||||
| elif line_id == b'map_ke': | elif line_id == b'map_ke': | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'Ke') | context_material_name, img_data, line, 'Ke') | ||||
| elif line_id in {b'map_bump', b'bump'}: # 'bump' is incorrect but some files use it. | elif line_id in {b'map_bump', b'bump'}: # 'bump' is incorrect but some files use it. | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'Bump') | context_material_name, img_data, line, 'Bump') | ||||
| elif line_id in {b'map_d', b'map_tr'}: # Alpha map - Dissolve | elif line_id in {b'map_d', b'map_tr'}: # Alpha map - Dissolve | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'D') | context_material_name, img_data, line, 'D') | ||||
| elif line_id in {b'map_disp', b'disp'}: # displacementmap | elif line_id in {b'map_disp', b'disp'}: # displacementmap | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'disp') | context_material_name, img_data, line, 'disp') | ||||
| elif line_id in {b'map_refl', b'refl'}: # reflectionmap | elif line_id in {b'map_refl', b'refl'}: # reflectionmap | ||||
| img_data = line.split()[1:] | img_data = line.split()[1:] | ||||
| if img_data: | if img_data: | ||||
| load_material_image(context_material, context_mat_wrap, use_cycles, | load_material_image(context_material, context_mat_wrap, | ||||
| context_material_name, img_data, line, 'refl') | context_material_name, img_data, line, 'refl') | ||||
| else: | else: | ||||
| print("\t%r:%r (ignored)" % (filepath, line)) | print("WARNING: %r:%r (ignored)" % (filepath, line)) | ||||
| mtl.close() | mtl.close() | ||||
| def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP): | def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP): | ||||
| """ | """ | ||||
| Takes vert_loc and faces, and separates into multiple sets of | Takes vert_loc and faces, and separates into multiple sets of | ||||
| (verts_loc, faces, unique_materials, dataname) | (verts_loc, faces, unique_materials, dataname) | ||||
| """ | """ | ||||
| ▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | |||||
| def create_mesh(new_objects, | def create_mesh(new_objects, | ||||
| use_edges, | use_edges, | ||||
| verts_loc, | verts_loc, | ||||
| verts_nor, | verts_nor, | ||||
| verts_tex, | verts_tex, | ||||
| faces, | faces, | ||||
| unique_materials, | unique_materials, | ||||
| unique_material_images, | |||||
| unique_smooth_groups, | unique_smooth_groups, | ||||
| vertex_groups, | vertex_groups, | ||||
| dataname, | dataname, | ||||
| ): | ): | ||||
| """ | """ | ||||
| Takes all the data gathered and generates a mesh, adding the new object to new_objects | Takes all the data gathered and generates a mesh, adding the new object to new_objects | ||||
| deals with ngons, sharp edges and assigning materials | deals with ngons, sharp edges and assigning materials | ||||
| """ | """ | ||||
| ▲ Show 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | def create_mesh(new_objects, | ||||
| me.polygons.foreach_set("loop_total", faces_loop_total) | me.polygons.foreach_set("loop_total", faces_loop_total) | ||||
| if verts_nor and me.loops: | if verts_nor and me.loops: | ||||
| # Note: we store 'temp' normals in loops, since validate() may alter final mesh, | # Note: we store 'temp' normals in loops, since validate() may alter final mesh, | ||||
| # we can only set custom lnors *after* calling it. | # we can only set custom lnors *after* calling it. | ||||
| me.create_normals_split() | me.create_normals_split() | ||||
| if verts_tex and me.polygons: | if verts_tex and me.polygons: | ||||
| me.uv_textures.new() | me.uv_layers.new() | ||||
| context_material_old = -1 # avoid a dict lookup | context_material_old = -1 # avoid a dict lookup | ||||
| mat = 0 # rare case it may be un-initialized. | mat = 0 # rare case it may be un-initialized. | ||||
| for i, (face, blen_poly) in enumerate(zip(faces, me.polygons)): | for i, (face, blen_poly) in enumerate(zip(faces, me.polygons)): | ||||
| if len(face[0]) < 3: | if len(face[0]) < 3: | ||||
| raise Exception("bad face") # Shall not happen, we got rid of those earlier! | raise Exception("bad face") # Shall not happen, we got rid of those earlier! | ||||
| Show All 15 Lines | for i, (face, blen_poly) in enumerate(zip(faces, me.polygons)): | ||||
| context_material_old = context_material | context_material_old = context_material | ||||
| blen_poly.material_index = mat | blen_poly.material_index = mat | ||||
| if verts_nor and face_vert_nor_indices: | if verts_nor and face_vert_nor_indices: | ||||
| for face_noidx, lidx in zip(face_vert_nor_indices, blen_poly.loop_indices): | for face_noidx, lidx in zip(face_vert_nor_indices, blen_poly.loop_indices): | ||||
| me.loops[lidx].normal[:] = verts_nor[0 if (face_noidx is ...) else face_noidx] | me.loops[lidx].normal[:] = verts_nor[0 if (face_noidx is ...) else face_noidx] | ||||
| if verts_tex and face_vert_tex_indices: | if verts_tex and face_vert_tex_indices: | ||||
| if context_material: | |||||
| image = unique_material_images[context_material] | |||||
| if image: # Can be none if the material dosnt have an image. | |||||
| me.uv_textures[0].data[i].image = image | |||||
| blen_uvs = me.uv_layers[0] | blen_uvs = me.uv_layers[0] | ||||
| for face_uvidx, lidx in zip(face_vert_tex_indices, blen_poly.loop_indices): | for face_uvidx, lidx in zip(face_vert_tex_indices, blen_poly.loop_indices): | ||||
| blen_uvs.data[lidx].uv = verts_tex[0 if (face_uvidx is ...) else face_uvidx] | blen_uvs.data[lidx].uv = verts_tex[0 if (face_uvidx is ...) else face_uvidx] | ||||
| use_edges = use_edges and bool(edges) | use_edges = use_edges and bool(edges) | ||||
| if use_edges: | if use_edges: | ||||
| me.edges.add(len(edges)) | me.edges.add(len(edges)) | ||||
| # edges should be a list of (a, b) tuples | # edges should be a list of (a, b) tuples | ||||
| Show All 20 Lines | if fgon_edges: | ||||
| bm.to_mesh(me) | bm.to_mesh(me) | ||||
| bm.free() | bm.free() | ||||
| # XXX If validate changes the geometry, this is likely to be broken... | # XXX If validate changes the geometry, this is likely to be broken... | ||||
| if unique_smooth_groups and sharp_edges: | if unique_smooth_groups and sharp_edges: | ||||
| for e in me.edges: | for e in me.edges: | ||||
| if e.key in sharp_edges: | if e.key in sharp_edges: | ||||
| e.use_edge_sharp = True | e.use_edge_sharp = True | ||||
| me.show_edge_sharp = True | |||||
| if verts_nor: | if verts_nor: | ||||
| clnors = array.array('f', [0.0] * (len(me.loops) * 3)) | clnors = array.array('f', [0.0] * (len(me.loops) * 3)) | ||||
| me.loops.foreach_get("normal", clnors) | me.loops.foreach_get("normal", clnors) | ||||
| if not unique_smooth_groups: | if not unique_smooth_groups: | ||||
| me.polygons.foreach_set("use_smooth", [True] * len(me.polygons)) | me.polygons.foreach_set("use_smooth", [True] * len(me.polygons)) | ||||
| me.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) | me.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) | ||||
| me.use_auto_smooth = True | me.use_auto_smooth = True | ||||
| me.show_edge_sharp = True | |||||
| ob = bpy.data.objects.new(me.name, me) | ob = bpy.data.objects.new(me.name, me) | ||||
| new_objects.append(ob) | new_objects.append(ob) | ||||
| # Create the vertex groups. No need to have the flag passed here since we test for the | # 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 | # content of the vertex_groups. If the user selects to NOT have vertex groups saved then | ||||
| # the following test will never run | # the following test will never run | ||||
| for group_name, group_indices in vertex_groups.items(): | for group_name, group_indices in vertex_groups.items(): | ||||
| ▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | def get_float_func(filepath): | ||||
| file.close() | file.close() | ||||
| # in case all vert values were ints | # in case all vert values were ints | ||||
| return float | return float | ||||
| def load(context, | def load(context, | ||||
| filepath, | filepath, | ||||
| *, | *, | ||||
| global_clamp_size=0.0, | global_clight_size=0.0, | ||||
| use_smooth_groups=True, | use_smooth_groups=True, | ||||
| use_edges=True, | use_edges=True, | ||||
| use_split_objects=True, | use_split_objects=True, | ||||
| use_split_groups=True, | use_split_groups=True, | ||||
| use_image_search=True, | use_image_search=True, | ||||
| use_groups_as_vgroups=False, | use_groups_as_vgroups=False, | ||||
| use_cycles=True, | |||||
| relpath=None, | relpath=None, | ||||
| global_matrix=None | global_matrix=None | ||||
| ): | ): | ||||
| """ | """ | ||||
| Called by the user interface or another script. | Called by the user interface or another script. | ||||
| load_obj(path) - should give acceptable results. | load_obj(path) - should give acceptable results. | ||||
| This function passes the file and sends the data off | This function passes the file and sends the data off | ||||
| to be split into objects and then converted into mesh objects | to be split into objects and then converted into mesh objects | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | with ProgressReport(context.window_manager) as progress: | ||||
| # Nurbs | # Nurbs | ||||
| context_nurbs = {} | context_nurbs = {} | ||||
| nurbs = [] | nurbs = [] | ||||
| context_parm = b'' # used by nurbs too but could be used elsewhere | context_parm = b'' # used by nurbs too but could be used elsewhere | ||||
| # Until we can use sets | # Until we can use sets | ||||
| unique_materials = {} | unique_materials = {} | ||||
| unique_material_images = {} | |||||
| unique_smooth_groups = {} | unique_smooth_groups = {} | ||||
| # unique_obects= {} - no use for this variable since the objects are stored in the face. | # unique_obects= {} - no use for this variable since the objects are stored in the face. | ||||
| # when there are faces that end with \ | # when there are faces that end with \ | ||||
| # it means they are multiline- | # it means they are multiline- | ||||
| # since we use xreadline we cant skip to the next line | # since we use xreadline we cant skip to the next line | ||||
| # so we need to know whether | # so we need to know whether | ||||
| context_multi_line = b'' | context_multi_line = b'' | ||||
| ▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | with ProgressReport(context.window_manager) as progress: | ||||
| ''' # How to use usemap? depricated? | ''' # How to use usemap? depricated? | ||||
| elif line_start == b'usema': # usemap or usemat | elif line_start == b'usema': # usemap or usemat | ||||
| context_image= line_value(line_split) | context_image= line_value(line_split) | ||||
| ''' | ''' | ||||
| progress.step("Done, loading materials and images...") | progress.step("Done, loading materials and images...") | ||||
| create_materials(filepath, relpath, material_libs, unique_materials, | create_materials(filepath, relpath, material_libs, unique_materials, | ||||
| unique_material_images, use_image_search, use_cycles, float_func) | use_image_search, float_func) | ||||
| progress.step("Done, building geometries (verts:%i faces:%i materials: %i smoothgroups:%i) ..." % | progress.step("Done, building geometries (verts:%i faces:%i materials: %i smoothgroups:%i) ..." % | ||||
| (len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups))) | (len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups))) | ||||
| # deselect all | # deselect all | ||||
| if bpy.ops.object.select_all.poll(): | if bpy.ops.object.select_all.poll(): | ||||
| bpy.ops.object.select_all(action='DESELECT') | bpy.ops.object.select_all(action='DESELECT') | ||||
| Show All 9 Lines | with ProgressReport(context.window_manager) as progress: | ||||
| #~ print(dataname, use_vnor, use_vtex) | #~ print(dataname, use_vnor, use_vtex) | ||||
| create_mesh(new_objects, | create_mesh(new_objects, | ||||
| use_edges, | use_edges, | ||||
| verts_loc_split, | verts_loc_split, | ||||
| verts_nor if use_vnor else [], | verts_nor if use_vnor else [], | ||||
| verts_tex if use_vtex else [], | verts_tex if use_vtex else [], | ||||
| faces_split, | faces_split, | ||||
| unique_materials_split, | unique_materials_split, | ||||
| unique_material_images, | |||||
| unique_smooth_groups, | unique_smooth_groups, | ||||
| vertex_groups, | vertex_groups, | ||||
| dataname, | dataname, | ||||
| ) | ) | ||||
| # nurbs support | # nurbs support | ||||
| for context_nurbs in nurbs: | for context_nurbs in nurbs: | ||||
| create_nurbs(context_nurbs, verts_loc, new_objects) | create_nurbs(context_nurbs, verts_loc, new_objects) | ||||
| view_layer = context.view_layer | |||||
| if view_layer.collections.active: | |||||
| collection = view_layer.collections.active.collection | |||||
| else: | |||||
| collection = scene.master_collection.new() | |||||
| view_layer.collections.link(collection) | |||||
| # Create new obj | # Create new obj | ||||
| for obj in new_objects: | for obj in new_objects: | ||||
| base = scene.objects.link(obj) | collection.objects.link(obj) | ||||
| base.select = True | obj.select_set('SELECT') | ||||
| # we could apply this anywhere before scaling. | # we could apply this anywhere before scaling. | ||||
| obj.matrix_world = global_matrix | obj.matrix_world = global_matrix | ||||
| scene.update() | scene.update() | ||||
| axis_min = [1000000000] * 3 | axis_min = [1000000000] * 3 | ||||
| axis_max = [-1000000000] * 3 | axis_max = [-1000000000] * 3 | ||||
| if global_clamp_size: | if global_clight_size: | ||||
| # Get all object bounds | # Get all object bounds | ||||
| for ob in new_objects: | for ob in new_objects: | ||||
| for v in ob.bound_box: | for v in ob.bound_box: | ||||
| for axis, value in enumerate(v): | for axis, value in enumerate(v): | ||||
| if axis_min[axis] > value: | if axis_min[axis] > value: | ||||
| axis_min[axis] = value | axis_min[axis] = value | ||||
| if axis_max[axis] < value: | if axis_max[axis] < value: | ||||
| axis_max[axis] = value | axis_max[axis] = value | ||||
| # Scale objects | # Scale objects | ||||
| max_axis = max(axis_max[0] - axis_min[0], axis_max[1] - axis_min[1], axis_max[2] - axis_min[2]) | 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 = 1.0 | ||||
| while global_clamp_size < max_axis * scale: | while global_clight_size < max_axis * scale: | ||||
| scale = scale / 10.0 | scale = scale / 10.0 | ||||
| for obj in new_objects: | for obj in new_objects: | ||||
| obj.scale = scale, scale, scale | obj.scale = scale, scale, scale | ||||
| progress.leave_substeps("Done.") | progress.leave_substeps("Done.") | ||||
| progress.leave_substeps("Finished importing: %r" % filepath) | progress.leave_substeps("Finished importing: %r" % filepath) | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||