Skip to content
Snippets Groups Projects
AutoOp.py 5.73 KiB
Newer Older
  • Learn to ignore specific revisions
  • # Copyright 2015 Théo Friberg under GNU GPL 3
    
    if "bpy" in locals():
    	import importlib
    	importlib.reload(JSONOps)
    else:
    	from . import JSONOps
    
    import bpy
    import os
    import glob
    
    class AutomatOperatorFromTexture(bpy.types.Operator):
    
    	"""This operator generates automatic materials from textures in Cycles."""
    
    	# Metadata of the operator
    
    	bl_idname = "com.new_automat"
    	bl_label = "Automatic Material from Image"
    	bl_options = {"UNDO"}
    
    	# Variables used for storing the filepath given by blender's file manager
    
    
    	filepath: bpy.props.StringProperty(subtype="FILE_PATH")
    	filename: bpy.props.StringProperty()
    	directory: bpy.props.StringProperty(subtype="FILE_PATH")
    
    	make_seamless: bpy.props.BoolProperty(name="Make Seamless", description="Make tileable (removes visible borders of the image).")
    
    
    	def execute(self, context):
    
    		"""This is the main runnable method of the operator.
    
    This creates all the node setup."""
    
    		# Create the material
    
    		mat = bpy.data.materials.new(self.filename)
    
    		mat.use_nodes = True
    		nodes = mat.node_tree.nodes
    
    		# Empty whatever nodes we allready had.
    
    		for node in nodes.keys():
    			nodes.remove(nodes[node])
    
    		nodes_dict = {}
    
    		# Create the main part of the material
    
    		nodes_dict = JSONOps.inflateFile(mat, os.path.dirname(
    									  os.path.realpath(__file__))+os.sep+
    									  "automatic_material.json")
    
    
    
    		# We load the images
    
    		image_data = bpy.data.images.load(self.filepath)
    		nodes_dict["Color Image"].image = image_data
    
    		# We check if the texture must be made seamless
    
    		if self.make_seamless:
    			seamless_vector = JSONOps.inflateFile(mat, os.path.dirname(os.path.realpath(__file__))+os.sep+"seamless_vector.json", -3000, 0)
    			links = mat.node_tree.links
    			links.new(seamless_vector["Pick Vector"].outputs["Color"], nodes_dict["Color Image"].inputs["Vector"])
    
    		# Below we check potential maps
    
    		modified_fname = self.filename.split(".")
    
    		# Check if we are dealing with maps generated with CrazyBump.
    		# If so is the case, the color map is by default suffixed with _COLOR
    
    		known_scheme = False
    
    		if modified_fname[0][-6:] == "_COLOR":
    
    			# We are dealing with CrazyBump and we remove the suffix
    
    			modified_fname[0] = modified_fname[0][:-6]
    			known_scheme = True
    
    		other_files = []
    		folder = os.path.split(self.filepath)[0]+os.path.sep+"*"
    		pattern = folder + ".".join(modified_fname[:-1])+"*."+modified_fname[-1]
    		other_files = glob.glob(pattern)
    
    		# We check if we can find a Specularity Map
    
    		specmap = ""
    
    		for file in other_files:
    			if "spec" in os.path.split(file)[-1].lower():
    				specmap = file
    				break
    
    		if len(specmap) > 0:
    
    			spec_map = nodes.new("ShaderNodeTexImage")
    			spec_map.location = [nodes_dict["Adjust reflectivity"].location[0],
    								 nodes_dict["Adjust reflectivity"].location[1]+50]
    			spec_map.label = "Specularity Map"
    			nodes.remove(nodes_dict["Adjust reflectivity"])
    			spec_map.image = bpy.data.images.load(specmap)
    			links = mat.node_tree.links
    
    			links.new(spec_map.outputs["Color"],
    					  nodes_dict["Mix Shaders"].inputs[0])
    			if self.make_seamless:
    				links.new(seamless_vector["Pick Vector"].outputs["Color"], spec_map.inputs["Vector"])
    
    		# We check if we can find a Normal Map
    
    		normalmap = ""
    
    		for file in other_files:
    			if "normal" in os.path.split(file)[-1].lower() or ".".join(os.path.split(file)[1].split(".") [:-1])[-4:] == "_NRM":
    				normalmap = file
    				break
    
    		if len(normalmap) > 0 and ((not "normal" in self.filename.lower()) or known_scheme):
    
    			normal_map = nodes.new("ShaderNodeTexImage")
    			normal_map.location = [nodes_dict["Color Image"].location[0],
    								   nodes_dict["Color Image"].location[1]-240]
    			normal_map.label = "Normal Map"
    			normal_map.image = bpy.data.images.load(normalmap)
    			links = mat.node_tree.links
    
    			normal = nodes.new("ShaderNodeNormalMap")
    			normal.location = [nodes_dict["Convert to Bump Map"].location[0],
    							   nodes_dict["Convert to Bump Map"].location[1]]
    			nodes.remove(nodes_dict["Convert to Bump Map"])
    			links.new(normal_map.outputs["Color"],
    					  normal.inputs[1])
    			links.new(normal.outputs["Normal"],
    					  nodes_dict["Diffuse Component"].inputs[2])
    			links.new(normal.outputs["Normal"],
    					  nodes_dict["Glossy Component"].inputs[2])
    			if self.make_seamless:
    				links.new(seamless_vector["Pick Vector"].outputs["Color"], normal_map.inputs["Vector"])
    
    		# We check if we can find a Bump Map
    
    		bumpmap = ""
    
    		for file in other_files:
    			if "bump" in os.path.split(file.lower())[-1]:
    				bumpmap = file
    				break
    
    		if len(bumpmap) > 0 and not "bump" in self.filename.lower() and not len(normalmap) > 0:
    
    			bump_map = nodes.new("ShaderNodeTexImage")
    			bump_map.location = [nodes_dict["Color Image"].location[0],
    								 nodes_dict["Color Image"].location[1]-240]
    			bump_map.label = "Bump Map"
    			bump_map.image = bpy.data.images.load(bumpmap)
    			links = mat.node_tree.links
    			links.new(bump_map.outputs["Color"], nodes_dict["Convert to Bump Map"].inputs[2])
    			if self.make_seamless:
    				links.new(seamless_vector["Pick Vector"].outputs["Color"], bump_map.inputs["Vector"])
    
    		# Try to add the material to the selected object
    		try:
    			bpy.context.object.data.materials.append(mat)
    		except AttributeError:
    
    			# If there is no object with materials selected,
    			# don't add the material to anythinng.
    
    			pass
    
    		# Tell that all went well
    
    		return {"FINISHED"}
    
    	def invoke(self, context, event):
    
    		"""This method opens the file browser. After that, the
    execute(...) method gets ran, creating the node setup.
    It also checks that the render engine is Cycles.  """
    
    		if bpy.context.scene.render.engine == 'CYCLES':
    			self.filename = ""
    			context.window_manager.fileselect_add(self)
    			return {"RUNNING_MODAL"}
    		else:
    			self.report({'ERROR'}, "Can't generate Cycles material with Blender"
    			"internal as active renderer.")
    			return {"FINISHED"}