Extensions: change the constant for the complete status
[blender-addons-contrib.git] / automat / AutoOp.py
blob76a99690177d983e4f06aac3972b840e2ad8c119
1 # Copyright 2015 Théo Friberg under GNU GPL 3
3 if "bpy" in locals():
4 import importlib
5 importlib.reload(JSONOps)
6 else:
7 from . import JSONOps
9 import bpy
10 import os
11 import glob
13 class AutomatOperatorFromTexture(bpy.types.Operator):
14 """This operator generates automatic materials from textures in Cycles"""
16 # Metadata of the operator
18 bl_idname = "com.new_automat"
19 bl_label = "Automatic Material from Image"
20 bl_options = {"UNDO"}
22 # Variables used for storing the filepath given by blender's file manager
24 filepath: bpy.props.StringProperty(subtype="FILE_PATH")
25 filename: bpy.props.StringProperty()
26 directory: bpy.props.StringProperty(subtype="FILE_PATH")
28 make_seamless: bpy.props.BoolProperty(
29 name="Make Seamless",
30 description="Make tileable (removes visible borders of the image)",
33 def execute(self, context):
35 """This is the main runnable method of the operator.
37 This creates all the node setup."""
39 # Create the material
41 mat = bpy.data.materials.new(self.filename)
43 mat.use_nodes = True
44 nodes = mat.node_tree.nodes
46 # Empty whatever nodes we allready had.
48 for node in nodes.keys():
49 nodes.remove(nodes[node])
51 nodes_dict = {}
53 # Create the main part of the material
55 nodes_dict = JSONOps.inflateFile(mat, os.path.dirname(
56 os.path.realpath(__file__))+os.sep+
57 "automatic_material.json")
61 # We load the images
63 image_data = bpy.data.images.load(self.filepath)
64 nodes_dict["Color Image"].image = image_data
66 # We check if the texture must be made seamless
68 if self.make_seamless:
69 seamless_vector = JSONOps.inflateFile(mat, os.path.dirname(os.path.realpath(__file__))+os.sep+"seamless_vector.json", -3000, 0)
70 links = mat.node_tree.links
71 links.new(seamless_vector["Pick Vector"].outputs["Color"], nodes_dict["Color Image"].inputs["Vector"])
73 # Below we check potential maps
75 modified_fname = self.filename.split(".")
77 # Check if we are dealing with maps generated with CrazyBump.
78 # If so is the case, the color map is by default suffixed with _COLOR
80 known_scheme = False
82 if modified_fname[0][-6:] == "_COLOR":
84 # We are dealing with CrazyBump and we remove the suffix
86 modified_fname[0] = modified_fname[0][:-6]
87 known_scheme = True
89 other_files = []
90 folder = os.path.split(self.filepath)[0]+os.path.sep+"*"
91 pattern = folder + ".".join(modified_fname[:-1])+"*."+modified_fname[-1]
92 other_files = glob.glob(pattern)
94 # We check if we can find a Specularity Map
96 specmap = ""
98 for file in other_files:
99 if "spec" in os.path.split(file)[-1].lower():
100 specmap = file
101 break
103 if len(specmap) > 0:
105 spec_map = nodes.new("ShaderNodeTexImage")
106 spec_map.location = [nodes_dict["Adjust reflectivity"].location[0],
107 nodes_dict["Adjust reflectivity"].location[1]+50]
108 spec_map.label = "Specularity Map"
109 nodes.remove(nodes_dict["Adjust reflectivity"])
110 spec_map.image = bpy.data.images.load(specmap)
111 links = mat.node_tree.links
113 links.new(spec_map.outputs["Color"],
114 nodes_dict["Mix Shaders"].inputs[0])
115 if self.make_seamless:
116 links.new(seamless_vector["Pick Vector"].outputs["Color"], spec_map.inputs["Vector"])
118 # We check if we can find a Normal Map
120 normalmap = ""
122 for file in other_files:
123 if "normal" in os.path.split(file)[-1].lower() or ".".join(os.path.split(file)[1].split(".") [:-1])[-4:] == "_NRM":
124 normalmap = file
125 break
127 if len(normalmap) > 0 and ((not "normal" in self.filename.lower()) or known_scheme):
129 normal_map = nodes.new("ShaderNodeTexImage")
130 normal_map.location = [nodes_dict["Color Image"].location[0],
131 nodes_dict["Color Image"].location[1]-240]
132 normal_map.label = "Normal Map"
133 normal_map.image = bpy.data.images.load(normalmap)
134 links = mat.node_tree.links
136 normal = nodes.new("ShaderNodeNormalMap")
137 normal.location = [nodes_dict["Convert to Bump Map"].location[0],
138 nodes_dict["Convert to Bump Map"].location[1]]
139 nodes.remove(nodes_dict["Convert to Bump Map"])
140 links.new(normal_map.outputs["Color"],
141 normal.inputs[1])
142 links.new(normal.outputs["Normal"],
143 nodes_dict["Diffuse Component"].inputs[2])
144 links.new(normal.outputs["Normal"],
145 nodes_dict["Glossy Component"].inputs[2])
146 if self.make_seamless:
147 links.new(seamless_vector["Pick Vector"].outputs["Color"], normal_map.inputs["Vector"])
149 # We check if we can find a Bump Map
151 bumpmap = ""
153 for file in other_files:
154 if "bump" in os.path.split(file.lower())[-1]:
155 bumpmap = file
156 break
158 if len(bumpmap) > 0 and not "bump" in self.filename.lower() and not len(normalmap) > 0:
160 bump_map = nodes.new("ShaderNodeTexImage")
161 bump_map.location = [nodes_dict["Color Image"].location[0],
162 nodes_dict["Color Image"].location[1]-240]
163 bump_map.label = "Bump Map"
164 bump_map.image = bpy.data.images.load(bumpmap)
165 links = mat.node_tree.links
166 links.new(bump_map.outputs["Color"], nodes_dict["Convert to Bump Map"].inputs[2])
167 if self.make_seamless:
168 links.new(seamless_vector["Pick Vector"].outputs["Color"], bump_map.inputs["Vector"])
170 # Try to add the material to the selected object
171 try:
172 bpy.context.object.data.materials.append(mat)
173 except AttributeError:
175 # If there is no object with materials selected,
176 # don't add the material to anythinng.
178 pass
180 # Tell that all went well
182 return {"FINISHED"}
184 def invoke(self, context, event):
186 """This method opens the file browser. After that, the
187 execute(...) method gets ran, creating the node setup.
188 It also checks that the render engine is Cycles. """
190 if bpy.context.scene.render.engine == 'CYCLES':
191 self.filename = ""
192 context.window_manager.fileselect_add(self)
193 return {"RUNNING_MODAL"}
194 else:
195 self.report({'ERROR'}, "Can't generate Cycles material with Blender"
196 "internal as active renderer.")
197 return {"FINISHED"}