Sfoglia il codice sorgente

several improvements in script shader generating

1. change transparent object's default refraction offset to (0.0, 0.0)
2. successfuly generating shader script even if no texture image
provided
3. if image texture node has no coordinate input, default to UV
4. remove glass effection in transparent principled BSDF
5. set a assertion guard in defining output sockets
6. correct emission and transparent bsdf output
7. more safe property check in mix shader
8. fix possible SameFileError in texture exporting
Jason0214 6 anni fa
parent
commit
abfed45b82

+ 27 - 22
io_scene_godot/converters/material/script_shader/node_converters.py

@@ -78,6 +78,7 @@ class Texture:
     """A texture"""
     """A texture"""
 
 
     def __init__(self, bl_image, identifier, hint_normal):
     def __init__(self, bl_image, identifier, hint_normal):
+        # note that image could be None, it need to be safely handled
         self.image = bl_image
         self.image = bl_image
         self.hint_normal = hint_normal
         self.hint_normal = hint_normal
         # identifier is the variable name in scripts
         # identifier is the variable name in scripts
@@ -173,10 +174,10 @@ class NodeConverterBase:
         self.variable_count += 1
         self.variable_count += 1
         return self._id_prefix + var_prefix + filter_id_illegal_char(hint)
         return self._id_prefix + var_prefix + filter_id_illegal_char(hint)
 
 
-    def generate_tmp_texture_id(self, texture_name):
+    def generate_tmp_texture_id(self, hashable_key):
         """generate a temp variable for texture, later it would be replaced
         """generate a temp variable for texture, later it would be replaced
         by uniform var"""
         by uniform var"""
-        var_prefix = "tex%s_" % hash(texture_name)
+        var_prefix = "tex%s_" % hash(hashable_key)
         return self._id_prefix + filter_id_illegal_char(var_prefix)
         return self._id_prefix + filter_id_illegal_char(var_prefix)
 
 
     def generate_socket_assignment(self, to_socket_id, to_socket_type,
     def generate_socket_assignment(self, to_socket_id, to_socket_type,
@@ -225,15 +226,16 @@ class NodeConverterBase:
             prop_b = input_b.get_property(pname)
             prop_b = input_b.get_property(pname)
 
 
             if pname == FragmentShaderLink.ALPHA:
             if pname == FragmentShaderLink.ALPHA:
-                # if one shader input has alpha, the other one should
+                # any shader node has no ALPHA property
                 # default to have alpha = 1.0
                 # default to have alpha = 1.0
-                if prop_a and not prop_b:
+                if prop_a is not None and prop_b is None:
                     prop_b = '1.0'
                     prop_b = '1.0'
-                elif prop_b and not prop_a:
+                elif prop_b is not None and prop_a is None:
                     prop_a = '1.0'
                     prop_a = '1.0'
 
 
-            mix_result_id = self.generate_variable_id_str('bsdf_' + pname)
-            if prop_a and prop_b:
+            output_socket = self.bl_node.outputs[0]
+            mix_result_id = self.generate_shader_id_str(output_socket, pname)
+            if prop_a is not None and prop_b is not None:
                 ptype = FragmentShaderLink.get_property_type(pname)
                 ptype = FragmentShaderLink.get_property_type(pname)
                 self.local_code.append(
                 self.local_code.append(
                     "%s = mix(%s, %s, %s)"
                     "%s = mix(%s, %s, %s)"
@@ -241,9 +243,9 @@ class NodeConverterBase:
                 )
                 )
 
 
                 output.set_property(pname, mix_result_id)
                 output.set_property(pname, mix_result_id)
-            elif prop_a:
+            elif prop_a is not None:
                 output.set_property(pname, prop_a)
                 output.set_property(pname, prop_a)
-            elif prop_b:
+            elif prop_b is not None:
                 output.set_property(pname, prop_b)
                 output.set_property(pname, prop_b)
 
 
     def add_function_call(self, function, in_args, out_args):
     def add_function_call(self, function, in_args, out_args):
@@ -422,6 +424,7 @@ class NodeConverterBase:
 
 
         for type_str, id_str in id_to_define:
         for type_str, id_str in id_to_define:
             # don't define if they already in input sockets
             # don't define if they already in input sockets
+            assert isinstance(id_str, str) and id_str.isidentifier()
             if id_str not in self._defined_ids:
             if id_str not in self._defined_ids:
                 self._defined_ids.add(id_str)
                 self._defined_ids.add(id_str)
                 self.output_definitions.append(
                 self.output_definitions.append(
@@ -499,8 +502,7 @@ class BsdfNodeConverter(NodeConverterBase):
         output_shader_link = FragmentShaderLink()
         output_shader_link = FragmentShaderLink()
         self.out_sockets_map[output_socket] = output_shader_link
         self.out_sockets_map[output_socket] = output_shader_link
 
 
-        if self.bl_node.bl_idname in \
-                ('ShaderNodeBsdfGlass', 'ShaderNodeBsdfPrincipled'):
+        if self.bl_node.bl_idname in ('ShaderNodeBsdfGlass',):
             self.flags.glass = True
             self.flags.glass = True
 
 
         if self.bl_node.bl_idname in \
         if self.bl_node.bl_idname in \
@@ -709,21 +711,24 @@ class ImageTextureNodeConverter(NodeConverterBase):
         function = find_node_function(self.bl_node)
         function = find_node_function(self.bl_node)
 
 
         in_arguments = list()
         in_arguments = list()
-        tex_coord = self.in_sockets_map[self.bl_node.inputs[0]]
+        tex_coord_socket = self.bl_node.inputs[0]
+        tex_coord = self.in_sockets_map[tex_coord_socket]
+        if not tex_coord_socket.is_linked:
+            # TODO: once generated texture get supported, it should be changed
+            logging.warning(
+                "No texture coordinate input for '%s' "
+                "UV is used by default, at '%s'",
+                self.bl_node.bl_idname, self.bl_node.name)
+            self.local_code.append("%s = vec3(UV, 0.0)" % tex_coord)
 
 
+        tex_var = self.generate_tmp_texture_id(self.bl_node.name)
         if self.bl_node.image is not None:
         if self.bl_node.image is not None:
-            tex_var = self.generate_tmp_texture_id(self.bl_node.image.name)
             is_normal = is_normal_texture(self.bl_node)
             is_normal = is_normal_texture(self.bl_node)
-            self.textures.append(
-                Texture(self.bl_node.image, tex_var, is_normal)
-            )
         else:
         else:
-            # it causes parsing error, so user can see it
-            tex_var = "NO_TEXTURE"
-            logging.error(
-                "No image selected for '%s', at'%s'",
-                self.bl_node.bl_idname, self.bl_node.name
-            )
+            is_normal = False
+        self.textures.append(
+            Texture(self.bl_node.image, tex_var, is_normal)
+        )
 
 
         in_arguments.append(tex_coord)
         in_arguments.append(tex_coord)
         in_arguments.append(tex_var)
         in_arguments.append(tex_var)

+ 31 - 15
io_scene_godot/converters/material/script_shader/node_tree.py

@@ -45,8 +45,9 @@ class ScriptShader:
     def add_fragment_output(self, output_shader_link):
     def add_fragment_output(self, output_shader_link):
         """link the node tree output with godot fragment output"""
         """link the node tree output with godot fragment output"""
         # pylint: disable-msg=too-many-branches
         # pylint: disable-msg=too-many-branches
+        # pylint: disable-msg=too-many-statements
 
 
-        # hack define those two variable at the begining
+        # hack: define those two variable at the begining
         self._fragment_code_lines.insert(0, "\n")
         self._fragment_code_lines.insert(0, "\n")
         if self.flags.inv_view_mat_used:
         if self.flags.inv_view_mat_used:
             self._fragment_code_lines.insert(
             self._fragment_code_lines.insert(
@@ -126,23 +127,30 @@ class ScriptShader:
         alpha = output_shader_link.get_property(FragmentShaderLink.ALPHA)
         alpha = output_shader_link.get_property(FragmentShaderLink.ALPHA)
         ior = output_shader_link.get_property(FragmentShaderLink.IOR)
         ior = output_shader_link.get_property(FragmentShaderLink.IOR)
         if self.flags.transparent and alpha is not None:
         if self.flags.transparent and alpha is not None:
-            # define an unifrom var
-            refraction_offset = 'refraction_offset'
-            self._uniform_code_lines.append(
-                'uniform vec2 %s = vec2(0.2, 0.2)' % refraction_offset
-            )
             if ior is not None and self.flags.glass:
             if ior is not None and self.flags.glass:
+                refraction_offset_value = (0.2, 0.2)
                 fresnel_func = find_function_by_name('refraction_fresnel')
                 fresnel_func = find_function_by_name('refraction_fresnel')
                 self._functions.add(fresnel_func)
                 self._functions.add(fresnel_func)
                 self._fragment_code_lines.append(
                 self._fragment_code_lines.append(
                     "refraction_fresnel(VERTEX, NORMAL, %s, %s)" %
                     "refraction_fresnel(VERTEX, NORMAL, %s, %s)" %
                     (ior, alpha)
                     (ior, alpha)
                 )
                 )
-            self._fragment_code_lines.append(
-                "EMISSION += textureLod(SCREEN_TEXTURE, SCREEN_UV - "
-                "NORMAL.xy * %s , ROUGHNESS).rgb * (1.0 - %s)" %
-                (refraction_offset, alpha)
-            )
+                refraction_offset = 'refraction_offset'
+                # just some magic random value, available for improvements
+                self._uniform_code_lines.append(
+                    'uniform vec2 %s = vec2(0.2, 0.2)' % refraction_offset
+                )
+                self._fragment_code_lines.append(
+                    "EMISSION += textureLod(SCREEN_TEXTURE, SCREEN_UV - "
+                    "NORMAL.xy * %s , ROUGHNESS).rgb * (1.0 - %s)" %
+                    (refraction_offset, alpha)
+                )
+            else:
+                self._fragment_code_lines.append(
+                    "EMISSION += textureLod(SCREEN_TEXTURE, SCREEN_UV, "
+                    "ROUGHNESS).rgb * (1.0 - %s)" % alpha
+                )
+
             self._fragment_code_lines.append(
             self._fragment_code_lines.append(
                 "ALBEDO *= %s" % alpha
                 "ALBEDO *= %s" % alpha
             )
             )
@@ -233,12 +241,19 @@ class ScriptShader:
 
 
     def get_images(self):
     def get_images(self):
         """return a set of all the images used in shader"""
         """return a set of all the images used in shader"""
-        return {tex.image for tex in self._textures}
+        image_set = set()
+        for tex in self._textures:
+            if tex.image is not None:
+                image_set.add(tex.image)
+        return image_set
 
 
     def get_image_texture_info(self):
     def get_image_texture_info(self):
         """return a list of tuple (image, texture uniform)"""
         """return a list of tuple (image, texture uniform)"""
-        return [(tex.image, uniform)
-                for tex, uniform in self._textures.items()]
+        image_uniform_tuples = list()
+        for tex, uniform in self._textures.items():
+            if tex.image is not None:
+                image_uniform_tuples.append((tex.image, uniform))
+        return image_uniform_tuples
 
 
 
 
 def find_material_output_node(node_tree):
 def find_material_output_node(node_tree):
@@ -306,7 +321,8 @@ def export_texture(escn_file, export_settings, image):
             src_path = bpy.path.abspath(image.filepath_raw)
             src_path = bpy.path.abspath(image.filepath_raw)
         else:
         else:
             src_path = image.filepath_raw
             src_path = image.filepath_raw
-        copyfile(src_path, dst_path)
+        if os.path.normpath(src_path) != os.path.normpath(dst_path):
+            copyfile(src_path, dst_path)
 
 
     img_resource = ExternalResource(dst_path, "Texture")
     img_resource = ExternalResource(dst_path, "Texture")
     return escn_file.add_external_resource(img_resource, image)
     return escn_file.add_external_resource(img_resource, image)

+ 7 - 3
io_scene_godot/converters/material/script_shader/shader_functions.py

@@ -128,12 +128,16 @@ void node_bsdf_principled(vec4 color, float subsurface, vec4 subsurface_color,
     BsdfShaderFunction(
     BsdfShaderFunction(
         code="""
         code="""
 void node_emission(vec4 emission_color, float strength,
 void node_emission(vec4 emission_color, float strength,
-        out vec3 emission_out) {
+        out vec3 emission_out, out float alpha_out) {
     emission_out = emission_color.rgb * strength;
     emission_out = emission_color.rgb * strength;
+    alpha_out = emission_color.a;
 }
 }
 """,
 """,
         input_sockets=["Color", "Strength"],
         input_sockets=["Color", "Strength"],
-        output_properties=[FragmentShaderLink.EMISSION]
+        output_properties=[
+            FragmentShaderLink.EMISSION,
+            FragmentShaderLink.ALPHA,
+        ]
     ),
     ),
 
 
     BsdfShaderFunction(
     BsdfShaderFunction(
@@ -179,7 +183,7 @@ void node_bsdf_glossy(vec4 color, float roughness, out vec3 albedo,
     BsdfShaderFunction(
     BsdfShaderFunction(
         code="""
         code="""
 void node_bsdf_transparent(vec4 color, out float alpha) {
 void node_bsdf_transparent(vec4 color, out float alpha) {
-    alpha = 0.0;
+    alpha = clamp(1.0 - dot(color.rgb, vec3(0.3333334)), 0.0, 1.0);
 }
 }
 """,
 """,
         input_sockets=['Color'],
         input_sockets=['Color'],

+ 23 - 24
tests/reference_exports/material_cycle/material_cycle.escn

@@ -143,20 +143,20 @@ void fragment () {
 	float node2_shader_in7_roughness = node0_bsdf_out2_roughness;
 	float node2_shader_in7_roughness = node0_bsdf_out2_roughness;
 	vec3 node2_shader_in8_normal = node0_in2_normal;
 	vec3 node2_shader_in8_normal = node0_in2_normal;
 	// output sockets definitions
 	// output sockets definitions
-	vec3 node2_var0_bsdf_albedo;
-	vec3 node2_var13_bsdf_normal;
+	vec3 node2_shader_out0_albedo;
+	vec3 node2_shader_out13_normal;
 	
 	
-	node2_var0_bsdf_albedo = mix(node2_shader_in1_albedo, node2_shader_in5_albedo,
-		node2_in0_fac);
-	node2_var13_bsdf_normal = mix(node2_shader_in4_normal, node2_shader_in8_normal,
+	node2_shader_out0_albedo = mix(node2_shader_in1_albedo, node2_shader_in5_albedo,
 		node2_in0_fac);
 		node2_in0_fac);
+	node2_shader_out13_normal = mix(node2_shader_in4_normal,
+		node2_shader_in8_normal, node2_in0_fac);
 	
 	
 	
 	
-	ALBEDO = node2_var0_bsdf_albedo;
+	ALBEDO = node2_shader_out0_albedo;
 	SPECULAR = node2_shader_in2_specular;
 	SPECULAR = node2_shader_in2_specular;
 	METALLIC = node2_shader_in6_metallic;
 	METALLIC = node2_shader_in6_metallic;
 	ROUGHNESS = node2_shader_in7_roughness;
 	ROUGHNESS = node2_shader_in7_roughness;
-	NORMAL = node2_var13_bsdf_normal;
+	NORMAL = node2_shader_out13_normal;
 	// uncomment it only when you set diffuse mode to oren nayar
 	// uncomment it only when you set diffuse mode to oren nayar
 	// ROUGHNESS = node2_shader_in3_oren_nayar_roughness;
 	// ROUGHNESS = node2_shader_in3_oren_nayar_roughness;
 }
 }
@@ -1147,20 +1147,20 @@ void fragment () {
 	float node2_shader_in6_roughness = node0_bsdf_out2_roughness;
 	float node2_shader_in6_roughness = node0_bsdf_out2_roughness;
 	vec3 node2_shader_in7_normal = node0_in2_normal;
 	vec3 node2_shader_in7_normal = node0_in2_normal;
 	// output sockets definitions
 	// output sockets definitions
-	vec3 node2_var0_bsdf_albedo;
-	vec3 node2_var13_bsdf_normal;
+	vec3 node2_shader_out0_albedo;
+	vec3 node2_shader_out13_normal;
 	
 	
-	node2_var0_bsdf_albedo = mix(node2_shader_in0_albedo, node2_shader_in4_albedo,
-		0.5);
-	node2_var13_bsdf_normal = mix(node2_shader_in3_normal, node2_shader_in7_normal,
+	node2_shader_out0_albedo = mix(node2_shader_in0_albedo, node2_shader_in4_albedo,
 		0.5);
 		0.5);
+	node2_shader_out13_normal = mix(node2_shader_in3_normal,
+		node2_shader_in7_normal, 0.5);
 	
 	
 	
 	
-	ALBEDO = node2_var0_bsdf_albedo;
+	ALBEDO = node2_shader_out0_albedo;
 	SPECULAR = node2_shader_in1_specular;
 	SPECULAR = node2_shader_in1_specular;
 	METALLIC = node2_shader_in5_metallic;
 	METALLIC = node2_shader_in5_metallic;
 	ROUGHNESS = node2_shader_in6_roughness;
 	ROUGHNESS = node2_shader_in6_roughness;
-	NORMAL = node2_var13_bsdf_normal;
+	NORMAL = node2_shader_out13_normal;
 	// uncomment it only when you set diffuse mode to oren nayar
 	// uncomment it only when you set diffuse mode to oren nayar
 	// ROUGHNESS = node2_shader_in2_oren_nayar_roughness;
 	// ROUGHNESS = node2_shader_in2_oren_nayar_roughness;
 }
 }
@@ -1219,13 +1219,13 @@ void fragment () {
 	vec3 node1_shader_in5_albedo = vec3(0.0, 0.0, 0.0);
 	vec3 node1_shader_in5_albedo = vec3(0.0, 0.0, 0.0);
 	vec3 node1_shader_in6_albedo = node1_shader_in5_albedo;
 	vec3 node1_shader_in6_albedo = node1_shader_in5_albedo;
 	// output sockets definitions
 	// output sockets definitions
-	vec3 node1_var0_bsdf_albedo;
+	vec3 node1_shader_out0_albedo;
 	
 	
-	node1_var0_bsdf_albedo = mix(node1_shader_in1_albedo, node1_shader_in6_albedo,
+	node1_shader_out0_albedo = mix(node1_shader_in1_albedo, node1_shader_in6_albedo,
 		node1_in0_fac);
 		node1_in0_fac);
 	
 	
 	
 	
-	ALBEDO = node1_var0_bsdf_albedo;
+	ALBEDO = node1_shader_out0_albedo;
 	SPECULAR = node1_shader_in2_specular;
 	SPECULAR = node1_shader_in2_specular;
 	NORMAL = node1_shader_in4_normal;
 	NORMAL = node1_shader_in4_normal;
 	// uncomment it only when you set diffuse mode to oren nayar
 	// uncomment it only when you set diffuse mode to oren nayar
@@ -1244,7 +1244,6 @@ resource_name = "Shader Nodetree"
 code = "shader_type spatial;
 code = "shader_type spatial;
 render_mode blend_mix, depth_draw_always, cull_back, diffuse_burley, specular_schlick_ggx;
 render_mode blend_mix, depth_draw_always, cull_back, diffuse_burley, specular_schlick_ggx;
 
 
-uniform vec2 refraction_offset = vec2(0.2, 0.2);
 
 
 
 
 void node_bsdf_diffuse(vec4 color, float roughness, out vec3 albedo,
 void node_bsdf_diffuse(vec4 color, float roughness, out vec3 albedo,
@@ -1256,7 +1255,7 @@ void node_bsdf_diffuse(vec4 color, float roughness, out vec3 albedo,
 
 
 
 
 void node_bsdf_transparent(vec4 color, out float alpha) {
 void node_bsdf_transparent(vec4 color, out float alpha) {
-    alpha = 0.0;
+    alpha = clamp(1.0 - dot(color.rgb, vec3(0.3333334)), 0.0, 1.0);
 }
 }
 
 
 void vertex () {
 void vertex () {
@@ -1301,9 +1300,9 @@ void fragment () {
 	vec3 node2_shader_in4_normal = node1_in2_normal;
 	vec3 node2_shader_in4_normal = node1_in2_normal;
 	float node2_shader_in5_alpha = node0_bsdf_out0_alpha;
 	float node2_shader_in5_alpha = node0_bsdf_out0_alpha;
 	// output sockets definitions
 	// output sockets definitions
-	float node2_var1_bsdf_alpha;
+	float node2_shader_out1_alpha;
 	
 	
-	node2_var1_bsdf_alpha = mix(1.0, node2_shader_in5_alpha, node2_in0_fac);
+	node2_shader_out1_alpha = mix(1.0, node2_shader_in5_alpha, node2_in0_fac);
 	
 	
 	
 	
 	ALBEDO = node2_shader_in1_albedo;
 	ALBEDO = node2_shader_in1_albedo;
@@ -1311,9 +1310,9 @@ void fragment () {
 	NORMAL = node2_shader_in4_normal;
 	NORMAL = node2_shader_in4_normal;
 	// uncomment it only when you set diffuse mode to oren nayar
 	// uncomment it only when you set diffuse mode to oren nayar
 	// ROUGHNESS = node2_shader_in3_oren_nayar_roughness;
 	// ROUGHNESS = node2_shader_in3_oren_nayar_roughness;
-	EMISSION += textureLod(SCREEN_TEXTURE, SCREEN_UV - NORMAL.xy * refraction_offset
-		, ROUGHNESS).rgb * (1.0 - node2_var1_bsdf_alpha);
-	ALBEDO *= node2_var1_bsdf_alpha;
+	EMISSION += textureLod(SCREEN_TEXTURE, SCREEN_UV, ROUGHNESS).rgb * (1.0 -
+		node2_shader_out1_alpha);
+	ALBEDO *= node2_shader_out1_alpha;
 	ALPHA = 1.0;
 	ALPHA = 1.0;
 }
 }
 "
 "