From 4101cb5d5aada3ec81ce6c1ce05f715cc1cf801b Mon Sep 17 00:00:00 2001
From: Bastien Montagne <montagne29@wanadoo.fr>
Date: Fri, 21 Sep 2018 20:02:41 +0200
Subject: [PATCH] Initial porting of OBJ (wavefront) IO add-on to 2.8.

As with FBX, major changes are no support for exporting materials
anymore (since all are nodals), and no more texface at all.
---
 io_scene_obj/__init__.py   | 79 ++++++++++++++++----------------
 io_scene_obj/export_obj.py | 14 +++---
 io_scene_obj/import_obj.py | 94 ++------------------------------------
 3 files changed, 49 insertions(+), 138 deletions(-)

diff --git a/io_scene_obj/__init__.py b/io_scene_obj/__init__.py
index 66329447c..c542b2d96 100644
--- a/io_scene_obj/__init__.py
+++ b/io_scene_obj/__init__.py
@@ -21,8 +21,8 @@
 bl_info = {
     "name": "Wavefront OBJ format",
     "author": "Campbell Barton, Bastien Montagne",
-    "version": (2, 3, 6),
-    "blender": (2, 78, 0),
+    "version": (3, 3, 6),
+    "blender": (2, 80, 0),
     "location": "File > Import-Export",
     "description": "Import-Export OBJ, Import OBJ mesh, UV's, materials and textures",
     "warning": "",
@@ -48,70 +48,68 @@ from bpy.props import (
 from bpy_extras.io_utils import (
         ImportHelper,
         ExportHelper,
-        orientation_helper_factory,
+        orientation_helper,
         path_reference_mode,
         axis_conversion,
         )
 
 
-IOOBJOrientationHelper = orientation_helper_factory("IOOBJOrientationHelper", axis_forward='-Z', axis_up='Y')
-
-
-class ImportOBJ(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
+@orientation_helper(axis_forward='-Z', axis_up='Y')
+class ImportOBJ(bpy.types.Operator, ImportHelper):
     """Load a Wavefront OBJ File"""
     bl_idname = "import_scene.obj"
     bl_label = "Import OBJ"
     bl_options = {'PRESET', 'UNDO'}
 
     filename_ext = ".obj"
-    filter_glob = StringProperty(
+    filter_glob: StringProperty(
             default="*.obj;*.mtl",
             options={'HIDDEN'},
             )
 
-    use_edges = BoolProperty(
+    use_edges: BoolProperty(
             name="Lines",
             description="Import lines and faces with 2 verts as edge",
             default=True,
             )
-    use_smooth_groups = BoolProperty(
+    use_smooth_groups: BoolProperty(
             name="Smooth Groups",
             description="Surround smooth groups by sharp edges",
             default=True,
             )
 
-    use_split_objects = BoolProperty(
+    use_split_objects: BoolProperty(
             name="Object",
             description="Import OBJ Objects into Blender Objects",
             default=True,
             )
-    use_split_groups = BoolProperty(
+    use_split_groups: BoolProperty(
             name="Group",
             description="Import OBJ Groups into Blender Objects",
             default=True,
             )
 
-    use_groups_as_vgroups = BoolProperty(
+    use_groups_as_vgroups: BoolProperty(
             name="Poly Groups",
             description="Import OBJ groups as vertex groups",
             default=False,
             )
 
-    use_image_search = BoolProperty(
+    use_image_search: BoolProperty(
             name="Image Search",
             description="Search subdirs for any associated images "
                         "(Warning, may be slow)",
             default=True,
             )
 
-    split_mode = EnumProperty(
+    split_mode: EnumProperty(
             name="Split",
             items=(('ON', "Split", "Split geometry, omits unused verts"),
                    ('OFF', "Keep Vert Order", "Keep vertex order from file"),
                    ),
             )
 
-    global_clight_size = FloatProperty(
+    global_clight_size: FloatProperty(
             name="Clamp Size",
             description="Clamp bounds under this value (zero to disable)",
             min=0.0, max=1000.0,
@@ -139,7 +137,7 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
                                         from_up=self.axis_up,
                                         ).to_4x4()
         keywords["global_matrix"] = global_matrix
-        keywords["use_cycles"] = (context.scene.view_render.engine == 'CYCLES')
+        keywords["use_cycles"] = True # (context.scene.view_render.engine == 'CYCLES')
 
         if bpy.data.is_saved and context.user_preferences.filepaths.use_relative_paths:
             import os
@@ -166,7 +164,7 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
         else:
             row.prop(self, "use_groups_as_vgroups")
 
-        row = layout.split(percentage=0.67)
+        row = layout.split(factor=0.67)
         row.prop(self, "global_clight_size")
         layout.prop(self, "axis_forward")
         layout.prop(self, "axis_up")
@@ -174,7 +172,8 @@ class ImportOBJ(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
         layout.prop(self, "use_image_search")
 
 
-class ExportOBJ(bpy.types.Operator, ExportHelper, IOOBJOrientationHelper):
+@orientation_helper(axis_forward='-Z', axis_up='Y')
+class ExportOBJ(bpy.types.Operator, ExportHelper):
     """Save a Wavefront OBJ File"""
 
     bl_idname = "export_scene.obj"
@@ -182,113 +181,113 @@ class ExportOBJ(bpy.types.Operator, ExportHelper, IOOBJOrientationHelper):
     bl_options = {'PRESET'}
 
     filename_ext = ".obj"
-    filter_glob = StringProperty(
+    filter_glob: StringProperty(
             default="*.obj;*.mtl",
             options={'HIDDEN'},
             )
 
     # context group
-    use_selection = BoolProperty(
+    use_selection: BoolProperty(
             name="Selection Only",
             description="Export selected objects only",
             default=False,
             )
-    use_animation = BoolProperty(
+    use_animation: BoolProperty(
             name="Animation",
             description="Write out an OBJ for each frame",
             default=False,
             )
 
     # object group
-    use_mesh_modifiers = BoolProperty(
+    use_mesh_modifiers: BoolProperty(
             name="Apply Modifiers",
             description="Apply modifiers",
             default=True,
             )
-    use_mesh_modifiers_render = BoolProperty(
+    use_mesh_modifiers_render: BoolProperty(
             name="Use Modifiers Render Settings",
             description="Use render settings when applying modifiers to mesh objects",
             default=False,
             )
 
     # extra data group
-    use_edges = BoolProperty(
+    use_edges: BoolProperty(
             name="Include Edges",
             description="",
             default=True,
             )
-    use_smooth_groups = BoolProperty(
+    use_smooth_groups: BoolProperty(
             name="Smooth Groups",
             description="Write sharp edges as smooth groups",
             default=False,
             )
-    use_smooth_groups_bitflags = BoolProperty(
+    use_smooth_groups_bitflags: BoolProperty(
             name="Bitflag Smooth Groups",
             description="Same as 'Smooth Groups', but generate smooth groups IDs as bitflags "
                         "(produces at most 32 different smooth groups, usually much less)",
             default=False,
             )
-    use_normals = BoolProperty(
+    use_normals: BoolProperty(
             name="Write Normals",
             description="Export one normal per vertex and per face, to represent flat faces and sharp edges",
             default=True,
             )
-    use_uvs = BoolProperty(
+    use_uvs: BoolProperty(
             name="Include UVs",
             description="Write out the active UV coordinates",
             default=True,
             )
-    use_materials = BoolProperty(
+    use_materials: BoolProperty(
             name="Write Materials",
             description="Write out the MTL file",
             default=True,
             )
-    use_triangles = BoolProperty(
+    use_triangles: BoolProperty(
             name="Triangulate Faces",
             description="Convert all faces to triangles",
             default=False,
             )
-    use_nurbs = BoolProperty(
+    use_nurbs: BoolProperty(
             name="Write Nurbs",
             description="Write nurbs curves as OBJ nurbs rather than "
                         "converting to geometry",
             default=False,
             )
-    use_vertex_groups = BoolProperty(
+    use_vertex_groups: BoolProperty(
             name="Polygroups",
             description="",
             default=False,
             )
 
     # grouping group
-    use_blen_objects = BoolProperty(
+    use_blen_objects: BoolProperty(
             name="Objects as OBJ Objects",
             description="",
             default=True,
             )
-    group_by_object = BoolProperty(
+    group_by_object: BoolProperty(
             name="Objects as OBJ Groups ",
             description="",
             default=False,
             )
-    group_by_material = BoolProperty(
+    group_by_material: BoolProperty(
             name="Material Groups",
             description="",
             default=False,
             )
-    keep_vertex_order = BoolProperty(
+    keep_vertex_order: BoolProperty(
             name="Keep Vertex Order",
             description="",
             default=False,
             )
 
-    global_scale = FloatProperty(
+    global_scale: FloatProperty(
             name="Scale",
             min=0.01, max=1000.0,
             default=1.0,
             )
 
-    path_mode = path_reference_mode
+    path_mode: path_reference_mode
 
     check_extension = True
 
@@ -303,7 +302,7 @@ class ExportOBJ(bpy.types.Operator, ExportHelper, IOOBJOrientationHelper):
                                             "filter_glob",
                                             ))
 
-        global_matrix = (Matrix.Scale(self.global_scale, 4) *
+        global_matrix = (Matrix.Scale(self.global_scale, 4) @
                          axis_conversion(to_forward=self.axis_forward,
                                          to_up=self.axis_up,
                                          ).to_4x4())
diff --git a/io_scene_obj/export_obj.py b/io_scene_obj/export_obj.py
index 22d688dab..8184067d0 100644
--- a/io_scene_obj/export_obj.py
+++ b/io_scene_obj/export_obj.py
@@ -69,7 +69,7 @@ def write_mtl(scene, filepath, path_mode, copy_set, mtl_dict):
 
             fw('\nnewmtl %s\n' % mtl_mat_name)  # Define a new material: matname_imgname
 
-            if mat:
+            if False and mat:  # XXX TODO Support nodal materials.
                 use_mirror = mat.raytrace_mirror.use and mat.raytrace_mirror.reflect_factor != 0.0
 
                 # convert from blenders spec to 0 - 1000 range.
@@ -139,7 +139,7 @@ def write_mtl(scene, filepath, path_mode, copy_set, mtl_dict):
                     # so we write the materials image.
                     face_img = None
 
-            if mat:  # No face image. if we havea material search for MTex image.
+            if False and mat:  # XXX TODO support nodal materials. If we have a material search for MTex image.
                 image_map = {}
                 # backwards so topmost are highest priority
                 for mtex in reversed(mat.texture_slots):
@@ -230,7 +230,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
 
@@ -379,7 +379,7 @@ def write_file(filepath, objects, depsgraph, 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
@@ -397,7 +397,7 @@ def write_file(filepath, objects, depsgraph, 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()
@@ -740,7 +740,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:
@@ -773,7 +773,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()
 
 
diff --git a/io_scene_obj/import_obj.py b/io_scene_obj/import_obj.py
index fb2731330..c2998c2ce 100644
--- a/io_scene_obj/import_obj.py
+++ b/io_scene_obj/import_obj.py
@@ -87,7 +87,7 @@ def obj_image_load(context_imagepath_map, line, DIR, recursive, relpath):
 
 
 def create_materials(filepath, relpath,
-                     material_libs, unique_materials, unique_material_images,
+                     material_libs, unique_materials,
                      use_image_search, use_cycles, float_func):
     """
     Create all the used materials in this obj,
@@ -133,48 +133,21 @@ def create_materials(filepath, relpath,
                 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':
             if use_cycles:
                 # XXX Not supported?
                 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':
             if use_cycles:
                 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':
             if use_cycles:
                 # XXX Not supported?
                 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':
             bump_mult = map_options.get(b'-bm')
             bump_mult = float(bump_mult[0]) if (bump_mult is not None and len(bump_mult) > 1) else 1.0
@@ -185,41 +158,16 @@ def create_materials(filepath, relpath,
                 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':
             if use_cycles:
                 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':
             if use_cycles:
                 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':
             map_type = map_options.get(b'-type')
             if map_type and map_type != [b'sphere']:
@@ -230,12 +178,6 @@ def create_materials(filepath, relpath,
                 mat_wrap.diffuse_image_set(image, 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:
             raise Exception("invalid type %r" % type)
 
@@ -263,18 +205,11 @@ def create_materials(filepath, relpath,
     for name in unique_materials:  # .keys()
         if name is not None:
             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.
             if use_cycles:
                 from modules import cycles_shader_compat
                 ma_wrap = cycles_shader_compat.CyclesShaderWrapper(ma)
                 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):
         # print(libname)
         mtlpath = os.path.join(DIR, libname)
@@ -382,22 +317,15 @@ def create_materials(filepath, relpath,
                         col = (float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3]))
                         if use_cycles:
                             context_mat_wrap.reflect_color_set(col)
-                        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':
                         col = (float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3]))
                         if use_cycles:
                             context_mat_wrap.diffuse_color_set(col)
-                        context_material.diffuse_color = col
-                        context_material.diffuse_intensity = 1.0
                     elif line_id == b'ks':
                         col = (float_func(line_split[1]), float_func(line_split[2]), float_func(line_split[3]))
                         if use_cycles:
                             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':
                         # We cannot set context_material.emit right now, we need final diffuse color as well for this.
                         emit_colors[:] = [
@@ -405,23 +333,15 @@ def create_materials(filepath, relpath,
                     elif line_id == b'ns':
                         if use_cycles:
                             context_mat_wrap.hardness_value_set(((float_func(line_split[1]) + 3.0) / 50.0) - 0.65)
-                        context_material.specular_hardness = int((float_func(line_split[1]) * 0.51) + 1)
                     elif line_id == b'ni':  # Refraction index (between 1 and 3).
                         if use_cycles:
                             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")
                     elif line_id == b'd':  # dissolve (transparency)
                         if use_cycles:
                             context_mat_wrap.alpha_value_set(float_func(line_split[1]))
-                        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
                         if use_cycles:
                             print("WARNING, currently unsupported translucency option, skipped.")
-                        context_material.translucency = float_func(line_split[1])
                     elif line_id == b'tf':
                         # rgb, filter color, blender has no support for this.
                         pass
@@ -601,7 +521,6 @@ def create_mesh(new_objects,
                 verts_tex,
                 faces,
                 unique_materials,
-                unique_material_images,
                 unique_smooth_groups,
                 vertex_groups,
                 dataname,
@@ -756,7 +675,7 @@ def create_mesh(new_objects,
         me.create_normals_split()
 
     if verts_tex and me.polygons:
-        me.uv_textures.new()
+        me.uv_layers.new()
 
     context_material_old = -1  # avoid a dict lookup
     mat = 0  # rare case it may be un-initialized.
@@ -788,11 +707,6 @@ def create_mesh(new_objects,
                 me.loops[lidx].normal[:] = verts_nor[0 if (face_noidx is ...) else face_noidx]
 
         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]
             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]
@@ -1036,7 +950,6 @@ def load(context,
 
         # Until we can use sets
         unique_materials = {}
-        unique_material_images = {}
         unique_smooth_groups = {}
         # unique_obects= {} - no use for this variable since the objects are stored in the face.
 
@@ -1258,7 +1171,7 @@ def load(context,
         progress.step("Done, loading materials and images...")
 
         create_materials(filepath, relpath, material_libs, unique_materials,
-                         unique_material_images, use_image_search, use_cycles, float_func)
+                         use_image_search, use_cycles, float_func)
 
         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)))
@@ -1284,7 +1197,6 @@ def load(context,
                         verts_tex if use_vtex else [],
                         faces_split,
                         unique_materials_split,
-                        unique_material_images,
                         unique_smooth_groups,
                         vertex_groups,
                         dataname,
-- 
GitLab