[Bf-extensions-cvs] [0cdaac6f] master: glTF exporter: Add option to keep original texture files

Julien Duroure noreply at git.blender.org
Sun Jul 4 17:55:41 CEST 2021


Commit: 0cdaac6f9a3e318b1d5db04ade2838d004cd500d
Author: Julien Duroure
Date:   Sun Jul 4 17:54:15 2021 +0200
Branches: master
https://developer.blender.org/rBA0cdaac6f9a3e318b1d5db04ade2838d004cd500d

glTF exporter: Add option to keep original texture files

WARNING: if you use more than one texture, where pbr standard requires only one,
only one texture will be used.
This can lead to unexpected results

===================================================================

M	io_scene_gltf2/__init__.py
M	io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
M	io_scene_gltf2/blender/exp/gltf2_blender_image.py

===================================================================

diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 4c758229..c2c028b9 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -15,7 +15,7 @@
 bl_info = {
     'name': 'glTF 2.0 format',
     'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
-    "version": (1, 7, 15),
+    "version": (1, 7, 16),
     'blender': (2, 91, 0),
     'location': 'File > Import-Export',
     'description': 'Import-Export as glTF 2.0',
@@ -173,6 +173,16 @@ class ExportGLTF2_Base:
         default='',
     )
 
+    export_keep_originals: BoolProperty(
+        name='Keep original',
+        description=('Keep original textures files if possible. '
+                     'WARNING: if you use more than one texture, '
+                     'where pbr standard requires only one, only one texture will be used.'
+                     'This can lead to unexpected results'
+        ),
+        default=False,
+    )
+
     export_texcoords: BoolProperty(
         name='UVs',
         description='Export UVs (texture coordinates) with meshes',
@@ -517,6 +527,7 @@ class ExportGLTF2_Base:
             export_settings['gltf_filedirectory'],
             self.export_texture_dir,
         )
+        export_settings['gltf_keep_original_textures'] = self.export_keep_originals
 
         export_settings['gltf_format'] = self.export_format
         export_settings['gltf_image_format'] = self.export_image_format
@@ -653,7 +664,10 @@ class GLTF_PT_export_main(bpy.types.Panel):
 
         layout.prop(operator, 'export_format')
         if operator.export_format == 'GLTF_SEPARATE':
-            layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
+            layout.prop(operator, 'export_keep_originals')
+            if operator.export_keep_originals is False:
+                layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER')
+
         layout.prop(operator, 'export_copyright')
         layout.prop(operator, 'will_save_settings')
 
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
index b0fb2c25..8e441f9c 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
@@ -42,7 +42,12 @@ def gather_image(
     mime_type = __gather_mime_type(blender_shader_sockets, image_data, export_settings)
     name = __gather_name(image_data, export_settings)
 
-    uri = __gather_uri(image_data, mime_type, name, export_settings)
+    if image_data.original is None:
+        uri = __gather_uri(image_data, mime_type, name, export_settings)
+    else:
+        # Retrieve URI relative to exported glTF files
+        uri = __gather_original_uri(image_data.original.filepath, export_settings)
+
     buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings)
 
     image = __make_image(
@@ -59,6 +64,27 @@ def gather_image(
 
     return image
 
+def __gather_original_uri(original_uri, export_settings):
+
+    def _path_to_uri(path):
+        import urllib
+        path = os.path.normpath(path)
+        path = path.replace(os.sep, '/')
+        return urllib.parse.quote(path)
+
+    path_to_image = bpy.path.abspath(original_uri)
+    if not os.path.exists(path_to_image): return None
+    try:
+        rel_path = os.path.relpath(
+            path_to_image,
+            start=export_settings[gltf2_blender_export_keys.FILE_DIRECTORY],
+        )
+    except ValueError:
+        # eg. because no relative path between C:\ and D:\ on Windows
+        return None
+    return _path_to_uri(rel_path)
+
+
 @cached
 def __make_image(buffer_view, extensions, extras, mime_type, name, uri, export_settings):
     return gltf2_io.Image(
@@ -99,7 +125,12 @@ def __gather_mime_type(sockets, export_image, export_settings):
             return "image/png"
 
     if export_settings["gltf_image_format"] == "AUTO":
-        image = export_image.blender_image()
+        if export_image.original is None: # We are going to create a new image
+            image = export_image.blender_image()
+        else:
+            # Using original image
+            image = export_image.original
+
         if image is not None and __is_blender_image_a_jpeg(image):
             return "image/jpeg"
         return "image/png"
@@ -109,30 +140,33 @@ def __gather_mime_type(sockets, export_image, export_settings):
 
 
 def __gather_name(export_image, export_settings):
-    # Find all Blender images used in the ExportImage
-    imgs = []
-    for fill in export_image.fills.values():
-        if isinstance(fill, FillImage):
-            img = fill.image
-            if img not in imgs:
-                imgs.append(img)
-
-    # If all the images have the same path, use the common filename
-    filepaths = set(img.filepath for img in imgs)
-    if len(filepaths) == 1:
-        filename = os.path.basename(list(filepaths)[0])
-        name, extension = os.path.splitext(filename)
-        if extension.lower() in ['.png', '.jpg', '.jpeg']:
-            if name:
-                return name
-
-    # Combine the image names: img1-img2-img3
-    names = []
-    for img in imgs:
-        name, extension = os.path.splitext(img.name)
-        names.append(name)
-    name = '-'.join(names)
-    return name or 'Image'
+    if export_image.original is None:
+        # Find all Blender images used in the ExportImage
+        imgs = []
+        for fill in export_image.fills.values():
+            if isinstance(fill, FillImage):
+                img = fill.image
+                if img not in imgs:
+                    imgs.append(img)
+
+        # If all the images have the same path, use the common filename
+        filepaths = set(img.filepath for img in imgs)
+        if len(filepaths) == 1:
+            filename = os.path.basename(list(filepaths)[0])
+            name, extension = os.path.splitext(filename)
+            if extension.lower() in ['.png', '.jpg', '.jpeg']:
+                if name:
+                    return name
+
+        # Combine the image names: img1-img2-img3
+        names = []
+        for img in imgs:
+            name, extension = os.path.splitext(img.name)
+            names.append(name)
+        name = '-'.join(names)
+        return name or 'Image'
+    else:
+        return export_image.original.name
 
 
 @cached
@@ -161,46 +195,55 @@ def __get_image_data(sockets, export_settings) -> ExportImage:
                                              result.shader_node.image))
             continue
 
-        # rudimentarily try follow the node tree to find the correct image data.
-        src_chan = Channel.R
-        for elem in result.path:
-            if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateRGB):
-                src_chan = {
-                    'R': Channel.R,
-                    'G': Channel.G,
-                    'B': Channel.B,
-                }[elem.from_socket.name]
-            if elem.from_socket.name == 'Alpha':
-                src_chan = Channel.A
-
-        dst_chan = None
-
-        # some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes)
-        if socket.name == 'Metallic':
-            dst_chan = Channel.B
-        elif socket.name == 'Roughness':
-            dst_chan = Channel.G
-        elif socket.name == 'Occlusion':
-            dst_chan = Channel.R
-        elif socket.name == 'Alpha':
-            dst_chan = Channel.A
-        elif socket.name == 'Clearcoat':
-            dst_chan = Channel.R
-        elif socket.name == 'Clearcoat Roughness':
-            dst_chan = Channel.G
-
-        if dst_chan is not None:
-            composed_image.fill_image(result.shader_node.image, dst_chan, src_chan)
-
-            # Since metal/roughness are always used together, make sure
-            # the other channel is filled.
-            if socket.name == 'Metallic' and not composed_image.is_filled(Channel.G):
-                composed_image.fill_white(Channel.G)
-            elif socket.name == 'Roughness' and not composed_image.is_filled(Channel.B):
-                composed_image.fill_white(Channel.B)
+        # Assume that user know what he does, and that channels/images are already combined correctly for pbr
+        # If not, we are going to keep only the first texture found
+        # Example : If user set up 2 or 3 different textures for Metallic / Roughness / Occlusion
+        # Only 1 will be used at export
+        # This Warning is displayed in UI of this option
+        if export_settings['gltf_keep_original_textures']:
+            composed_image = ExportImage.from_original(result.shader_node.image)
+
         else:
-            # copy full image...eventually following sockets might overwrite things
-            composed_image = ExportImage.from_blender_image(result.shader_node.image)
+            # rudimentarily try follow the node tree to find the correct image data.
+            src_chan = Channel.R
+            for elem in result.path:
+                if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateRGB):
+                    src_chan = {
+                        'R': Channel.R,
+                        'G': Channel.G,
+                        'B': Channel.B,
+                    }[elem.from_socket.name]
+                if elem.from_socket.name == 'Alpha':
+                    src_chan = Channel.A
+
+            dst_chan = None
+
+            # some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes)
+            if socket.name == 'Metallic':
+                dst_chan = Channel.B
+            elif socket.name == 'Roughness':
+                dst_chan = Channel.G
+            elif socket.name == 'Occlusion':
+                dst_chan = Channel.R
+            elif socket.name == 'Alpha':
+                dst_chan = Channel.A
+            elif socket.name == 'Clearcoat':
+                dst_chan = Channel.R
+            elif socket.name == 'Clearcoat Roughness':
+                dst_chan = Channel.G
+
+            if dst_chan is not None:
+                composed_image.fill_image(re

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list