Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Duroure <julien.duroure@gmail.com>2020-01-08 23:31:52 +0300
committerJulien Duroure <julien.duroure@gmail.com>2020-01-08 23:31:52 +0300
commit569f96c037e5dfefd9e1127dca4de92746d99c31 (patch)
tree5f54d855edc9360d1d46a2852352d6621860b9d5 /io_scene_gltf2/blender/exp/gltf2_blender_image.py
parent9733d0f73654ea6b7d93c8b3e4c9eceebb644520 (diff)
glTF exporter: perf improvement in texture export
Diffstat (limited to 'io_scene_gltf2/blender/exp/gltf2_blender_image.py')
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_image.py126
1 files changed, 125 insertions, 1 deletions
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_image.py
index e0eecd3c..4a7818fe 100644
--- a/io_scene_gltf2/blender/exp/gltf2_blender_image.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_image.py
@@ -119,7 +119,98 @@ class ExportImage:
return self.__encode_from_image(fill.image)
def __encode_unhappy(self) -> bytes:
- # This will be a numpy array we fill in with pixel data.
+ result = self.__encode_unhappy_with_compositor()
+ if result is not None:
+ return result
+ return self.__encode_unhappy_with_numpy()
+
+ def __encode_unhappy_with_compositor(self) -> bytes:
+ # Builds a Compositor graph that will build the correct image
+ # from the description in self.fills.
+ #
+ # [ Image ]->[ Sep RGBA ] [ Comb RGBA ]
+ # [ src_chan]--->[dst_chan ]--->[ Output ]
+ #
+ # This is hacky, but is about 4x faster than using
+ # __encode_unhappy_with_numpy. There are some caveats though:
+
+ # First, we can't handle pre-multiplied alpha.
+ if Channel.A in self.fills:
+ return None
+
+ # Second, in order to get the same results as using image.pixels
+ # (which ignores the colorspace), we need to use the 'Non-Color'
+ # colorspace for all images and set the output device to 'None'. But
+ # setting the colorspace on dirty images discards their changes.
+ # So we can't handle dirty images that aren't already 'Non-Color'.
+ for fill in self.fills:
+ if isinstance(fill, FillImage):
+ if fill.image.is_dirty:
+ if fill.image.colorspace_settings.name != 'Non-Color':
+ return None
+
+ tmp_scene = None
+ orig_colorspaces = {} # remembers original colorspaces
+ try:
+ tmp_scene = bpy.data.scenes.new('##gltf-export:tmp-scene##')
+ tmp_scene.use_nodes = True
+ node_tree = tmp_scene.node_tree
+ for node in node_tree.nodes:
+ node_tree.nodes.remove(node)
+
+ out = node_tree.nodes.new('CompositorNodeComposite')
+ comb_rgba = node_tree.nodes.new('CompositorNodeCombRGBA')
+ for i in range(4):
+ comb_rgba.inputs[i].default_value = 1.0
+ node_tree.links.new(out.inputs['Image'], comb_rgba.outputs['Image'])
+
+ img_size = None
+ for dst_chan, fill in self.fills.items():
+ if not isinstance(fill, FillImage):
+ continue
+
+ img = node_tree.nodes.new('CompositorNodeImage')
+ img.image = fill.image
+ sep_rgba = node_tree.nodes.new('CompositorNodeSepRGBA')
+ node_tree.links.new(sep_rgba.inputs['Image'], img.outputs['Image'])
+ node_tree.links.new(comb_rgba.inputs[dst_chan], sep_rgba.outputs[fill.src_chan])
+
+ if fill.image.colorspace_settings.name != 'Non-Color':
+ if fill.image.name not in orig_colorspaces:
+ orig_colorspaces[fill.image.name] = \
+ fill.image.colorspace_settings.name
+ fill.image.colorspace_settings.name = 'Non-Color'
+
+ if img_size is None:
+ img_size = fill.image.size[:2]
+ else:
+ # All images should be the same size (should be
+ # guaranteed by gather_texture_info)
+ assert img_size == fill.image.size[:2]
+
+ width, height = img_size or (1, 1)
+ return _render_temp_scene(
+ tmp_scene=tmp_scene,
+ width=width,
+ height=height,
+ file_format=self.file_format,
+ color_mode='RGB',
+ colorspace='None',
+ )
+
+ finally:
+ for img_name, colorspace in orig_colorspaces.items():
+ bpy.data.images[img_name].colorspace_settings.name = colorspace
+
+ if tmp_scene is not None:
+ bpy.data.scenes.remove(tmp_scene, do_unlink=True)
+
+
+ def __encode_unhappy_with_numpy(self):
+ # Read the pixels of each image with image.pixels, put them into a
+ # numpy, and assemble the desired image that way. This is the slowest
+ # method, and the conversion to Python data eats a lot of memory, so
+ # it's only used as a last resort.
result = None
img_fills = {
@@ -217,3 +308,36 @@ def _encode_temp_image(tmp_image: bpy.types.Image, file_format: str) -> bytes:
with open(tmpfilename, "rb") as f:
return f.read()
+
+def _render_temp_scene(
+ tmp_scene: bpy.types.Scene,
+ width: int,
+ height: int,
+ file_format: str,
+ color_mode: str,
+ colorspace: str,
+) -> bytes:
+ """Set render settings, render to a file, and read back."""
+ tmp_scene.render.resolution_x = width
+ tmp_scene.render.resolution_y = height
+ tmp_scene.render.resolution_percentage = 100
+ tmp_scene.display_settings.display_device = colorspace
+ tmp_scene.render.image_settings.color_mode = color_mode
+ tmp_scene.render.dither_intensity = 0.0
+
+ # Turn off all metadata (stuff like use_stamp_date, etc.)
+ for attr in dir(tmp_scene.render):
+ if attr.startswith('use_stamp_'):
+ setattr(tmp_scene.render, attr, False)
+
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ tmpfilename = tmpdirname + "/img"
+ tmp_scene.render.filepath = tmpfilename
+ tmp_scene.render.use_file_extension = False
+ tmp_scene.render.image_settings.file_format = file_format
+
+ bpy.ops.render.render(scene=tmp_scene.name, write_still=True)
+
+ with open(tmpfilename, "rb") as f:
+ return f.read()
+