diff options
Diffstat (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py')
-rw-r--r-- | io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py new file mode 100644 index 00000000..3441b5ad --- /dev/null +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py @@ -0,0 +1,356 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2021 The glTF-Blender-IO authors. + +import bpy +from ...io.com.gltf2_io import TextureInfo +from .gltf2_blender_texture import texture +from io_scene_gltf2.io.com.gltf2_io_constants import GLTF_IOR +from .gltf2_blender_image import BlenderImage +from ..exp.gltf2_blender_image import TmpImageGuard, make_temp_image_copy + + +def specular(mh, location_specular, + location_specular_tint, + specular_socket, + specular_tint_socket, + original_specular_socket, + original_specularcolor_socket, + location_original_specular, + location_original_specularcolor): + x_specular, y_specular = location_specular + x_tint, y_tint = location_specular_tint + + if specular_socket is None: + return + if specular_tint_socket is None: + return + + try: + ext = mh.pymat.extensions['KHR_materials_specular'] + except Exception: + return + + import numpy as np + + # Retrieve image names + try: + tex_info = mh.pymat.pbr_metallic_roughness.base_color_texture + pytexture = mh.gltf.data.textures[tex_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + base_color_image_name = pyimg.blender_image_name + except: + base_color_image_name = None + + # First check if we need a texture or not -> retrieve all info needed + specular_factor = ext.get('specularFactor', 1.0) + tex_specular_info = ext.get('specularTexture') + if tex_specular_info is not None: + tex_specular_info = TextureInfo.from_dict(tex_specular_info) + + specular_color_factor = np.array(ext.get('specularColorFactor', [1.0, 1.0, 1.0])[:3]) + tex_specular_color_info = ext.get('specularColorTexture') + if tex_specular_color_info is not None: + tex_specular_color_info = TextureInfo.from_dict(tex_specular_color_info) + + base_color_not_linked = base_color_image_name is None + base_color = np.array(mh.pymat.pbr_metallic_roughness.base_color_factor or [1, 1, 1]) + tex_base_color = mh.pymat.pbr_metallic_roughness.base_color_texture + base_color = base_color[:3] + + try: + ext_transmission = mh.pymat.extensions['KHR_materials_transmission'] + transmission_factor = ext_transmission.get('transmissionFactor', 0) + tex_transmission_info = ext_transmission.get('transmissionTexture') + if tex_transmission_info is not None: + tex_transmission_info = TextureInfo.from_dict(tex_transmission_info) + pytexture = mh.gltf.data.textures[tex_transmission_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + transmission_image_name = pyimg.blender_image_name + else: + transmission_image_name = None + except Exception: + transmission_factor = 0 + tex_transmission_info = None + transmission_image_name = None + + transmission_not_linked = transmission_image_name is None + + try: + ext_ior = mh.pymat.extensions['KHR_materials_ior'] + ior = ext_ior.get('ior', GLTF_IOR) + except: + ior = GLTF_IOR + + use_texture = tex_specular_info is not None or tex_specular_color_info is not None \ + or transmission_not_linked is False or base_color_not_linked is False + + + # Before creating converted textures, + # Also plug non converted data into glTF PBR Non Converted Extensions node + original_specular( mh, + specular_factor, + tex_specular_info, + specular_color_factor, + tex_specular_color_info, + original_specular_socket, + original_specularcolor_socket, + location_original_specular, + location_original_specularcolor + ) + + + if not use_texture: + + def luminance(c): + return 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2] + + def normalize(c): + assert(len(c) == 3) + l = luminance(c) + if l == 0: + return c + return np.array([c[0] / l, c[1] / l, c[2] / l]) + + f0_from_ior = ((ior - 1)/(ior + 1))**2 + lum_specular_color = luminance(specular_color_factor) + blender_specular = ((lum_specular_color - transmission_factor) / (1 - transmission_factor)) * (1 / 0.08) * f0_from_ior + if not all([i == 0 for i in normalize(base_color) - 1]): + blender_specular_tint = luminance((normalize(specular_color_factor) - 1) / (normalize(base_color) - 1)) + if blender_specular_tint < 0 or blender_specular_tint > 1: + # TODOExt Warning clamping + blender_specular_tint = np.maximum(np.minimum(blender_specular_tint, 1), 0) + else: + blender_specular_tint = 1.0 + + specular_socket.default_value = blender_specular + specular_tint_socket.default_value = blender_specular_tint + # Note: blender_specular can be greater 1. The Blender documentation permits this. + + return + else: + # Need to create a texture + # First, retrieve and create all images needed + + # Base Color is already created + # Transmission is already created + # specularTexture is just created by original specular function + specular_image_name = None + try: + pytexture = mh.gltf.data.textures[tex_specular_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + specular_image_name = pyimg.blender_image_name + except: + specular_image_name = None + + + # specularColorTexture is just created by original specular function + specularcolor_image_name = None + try: + pytexture = mh.gltf.data.textures[tex_specular_color_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + specularcolor_image_name = pyimg.blender_image_name + except: + specularcolor_image_name = None + + stack3 = lambda v: np.dstack([v]*3) + + texts = { + base_color_image_name : 'basecolor', + transmission_image_name : 'transmission', + specularcolor_image_name : 'speccolor', + specular_image_name: 'spec' + } + images = [(name, bpy.data.images[name]) for name in [base_color_image_name, transmission_image_name, specularcolor_image_name, specular_image_name] if name is not None] + + width = max(image[1].size[0] for image in images) + height = max(image[1].size[1] for image in images) + + buffers = {} + for name, image in images: + tmp_buf = np.empty(width * height * 4, np.float32) + + if image.size[0] == width and image.size[1] == height: + image.pixels.foreach_get(tmp_buf) + else: + # Image is the wrong size; make a temp copy and scale it. + with TmpImageGuard() as guard: + make_temp_image_copy(guard, src_image=image) + tmp_image = guard.image + tmp_image.scale(width, height) + tmp_image.pixels.foreach_get(tmp_buf) + + buffers[texts[name]] = np.reshape(tmp_buf, [width, height, 4]) + buffers[texts[name]] = buffers[texts[name]][:,:,:3] + + # Manage factors + if name == transmission_image_name: + buffers[texts[name]] = stack3(buffers[texts[name]][:,:,0]) # Transmission : keep only R channel + + buffers[texts[name]] *= stack3(transmission_factor) + + elif name == base_color_image_name: + buffers[texts[name]] *= base_color + + elif name == specularcolor_image_name: + buffers[texts[name]] *= specular_color_factor + + # Create buffer if there is no image + if 'basecolor' not in buffers.keys(): + buffers['basecolor'] = np.full((width, height, 3), base_color) + if 'transmission' not in buffers.keys(): + buffers['transmission'] = np.full((width, height, 3), transmission_factor) + if 'speccolor' not in buffers.keys(): + buffers['speccolor'] = np.full((width, height, 3), specular_color_factor) + + # Calculation + + luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2] + def normalize(c): + l = luminance(c) + if np.all(l == 0.0): + return np.array(c) + return c / stack3(l) + + f0_from_ior = ((ior - 1)/(ior + 1))**2 + lum_specular_color = stack3(luminance(buffers['speccolor'])) + blender_specular = ((lum_specular_color - buffers['transmission']) / (1 - buffers['transmission'])) * (1 / 0.08) * f0_from_ior + if not np.all(normalize(buffers['basecolor']) - 1 == 0.0): + blender_specular_tint = luminance((normalize(buffers['speccolor']) - 1) / (normalize(buffers['basecolor']) - 1)) + np.nan_to_num(blender_specular_tint, copy=False) + blender_specular_tint = np.clip(blender_specular_tint, 0.0, 1.0) + blender_specular_tint = stack3(blender_specular_tint) + else: + blender_specular_tint = stack3(np.ones((width, height))) + + blender_specular = np.dstack((blender_specular, np.ones((width, height)))) # Set alpha to 1 + blender_specular_tint = np.dstack((blender_specular_tint, np.ones((width, height)))) # Set alpha to 1 + + # Check if we really need to create a texture + blender_specular_tex_not_needed = np.all(np.isclose(blender_specular, blender_specular[0][0])) + blender_specular_tint_tex_not_needed = np.all(np.isclose(blender_specular_tint, blender_specular_tint[0][0])) + + if blender_specular_tex_not_needed == True: + lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2] + specular_socket.default_value = lum(blender_specular[0][0][:3]) + else: + blender_specular = np.reshape(blender_specular, width * height * 4) + # Create images in Blender, width and height are dummy values, then set packed file data + blender_image_spec = bpy.data.images.new('Specular', width, height) + blender_image_spec.pixels.foreach_set(np.float32(blender_specular)) + blender_image_spec.pack() + + # Create Textures in Blender + tex_info = tex_specular_info + if tex_info is None: + tex_info = tex_specular_color_info + if tex_info is None: + tex_info = tex_transmission_info + if tex_info is None: + tex_info = tex_base_color + + texture( + mh, + tex_info=tex_info, + label='SPECULAR', + location=(x_specular, y_specular), + is_data=True, + color_socket=specular_socket, + forced_image=blender_image_spec + ) + + if blender_specular_tint_tex_not_needed == True: + lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2] + specular_tint_socket.default_value = lum(blender_specular_tint[0][0]) + else: + blender_specular_tint = np.reshape(blender_specular_tint, width * height * 4) + # Create images in Blender, width and height are dummy values, then set packed file data + blender_image_tint = bpy.data.images.new('Specular Tint', width, height) + blender_image_tint.pixels.foreach_set(np.float32(blender_specular_tint)) + blender_image_tint.pack() + + # Create Textures in Blender + tex_info = tex_specular_color_info + if tex_info is None: + tex_info = tex_specular_info + if tex_info is None: + tex_info = tex_transmission_info + if tex_info is None: + tex_info = tex_base_color + + texture( + mh, + tex_info=tex_info, + label='SPECULAR TINT', + location=(x_tint, y_tint), + is_data=True, + color_socket=specular_tint_socket, + forced_image=blender_image_tint + ) + +def original_specular( mh, + specular_factor, + tex_specular_info, + specular_color_factor, + tex_specular_color_info, + original_specular_socket, + original_specularcolor_socket, + location_original_specular, + location_original_specularcolor + ): + + x_specular, y_specular = location_original_specular + x_specularcolor, y_specularcolor = location_original_specularcolor + + if tex_specular_info is None: + original_specular_socket.default_value = specular_factor + else: + # Mix specular factor + if specular_factor != 1.0: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Specular Factor' + node.location = x_specular - 140, y_specular + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(original_specular_socket, node.outputs[0]) + # Inputs + original_specular_socket = node.inputs[0] + node.inputs[1].default_value = specular_factor + x_specular -= 200 + + texture( + mh, + tex_info=tex_specular_info, + label='SPECULAR', + location=(x_specular, y_specular), + is_data=True, + color_socket=None, + alpha_socket=original_specular_socket + ) + + if tex_specular_color_info is None: + specular_color_factor = list(specular_color_factor) + specular_color_factor.extend([1.0]) + original_specularcolor_socket.default_value = specular_color_factor + else: + specular_color_factor = list(specular_color_factor) + [1.0] + if specular_color_factor != [1.0, 1.0, 1.0, 1.0]: + # Mix specularColorFactor + node = mh.node_tree.nodes.new('ShaderNodeMixRGB') + node.label = 'SpecularColor Factor' + node.location = x_specularcolor - 140, y_specularcolor + node.blend_type = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(original_specularcolor_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1.0 + original_specularcolor_socket = node.inputs['Color1'] + node.inputs['Color2'].default_value = specular_color_factor + x_specularcolor -= 200 + + texture( + mh, + tex_info=tex_specular_color_info, + label='SPECULAR COLOR', + location=(x_specularcolor, y_specularcolor), + color_socket=original_specularcolor_socket, + )
\ No newline at end of file |