Cycles: rename displacement methods, default to displace, tweak panel layout.
[blender.git] / intern / cycles / blender / addon / version_update.py
1 #
2 # Copyright 2011-2014 Blender Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16
17 # <pep8 compliant>
18
19 import bpy
20
21 from bpy.app.handlers import persistent
22
23
24 def check_is_new_shading_ntree(node_tree):
25     for node in node_tree.nodes:
26         # If material has any node with ONLY new shading system
27         # compatibility then it's considered a Cycles material
28         # and versioning code would need to perform on it.
29         #
30         # We can not check for whether NEW_SHADING in compatibility
31         # because some nodes could have compatibility with both old
32         # and new shading system and they can't be used for any
33         # decision here.
34         if node.shading_compatibility == {'NEW_SHADING'}:
35             return True
36
37         # If node is only compatible with old shading system
38         # then material can not be Cycles material and we
39         # can stopiterating nodes now.
40         if node.shading_compatibility == {'OLD_SHADING'}:
41             return False
42     return False
43
44
45 def check_is_new_shading_material(material):
46     if not material.node_tree:
47         return False
48     return check_is_new_shading_ntree(material.node_tree)
49
50
51 def check_is_new_shading_world(world):
52     if not world.node_tree:
53         return False
54     return check_is_new_shading_ntree(world.node_tree)
55
56
57 def check_is_new_shading_lamp(lamp):
58     if not lamp.node_tree:
59         return False
60     return check_is_new_shading_ntree(lamp.node_tree)
61
62
63 def foreach_notree_node(nodetree, callback, traversed):
64     if nodetree in traversed:
65         return
66     traversed.add(nodetree)
67     for node in nodetree.nodes:
68         callback(node)
69         if node.bl_idname == 'ShaderNodeGroup':
70             foreach_notree_node(node.node_tree, callback, traversed)
71
72
73 def foreach_cycles_node(callback):
74     traversed = set()
75     for material in bpy.data.materials:
76         if check_is_new_shading_material(material):
77                 foreach_notree_node(material.node_tree,
78                                     callback,
79                                     traversed)
80     for world in bpy.data.worlds:
81         if check_is_new_shading_world(world):
82                 foreach_notree_node(world.node_tree,
83                                     callback,
84                                     traversed)
85     for lamp in bpy.data.lamps:
86         if check_is_new_shading_world(lamp):
87                 foreach_notree_node(lamp.node_tree,
88                                     callback,
89                                     traversed)
90
91
92 def displacement_node_insert(material, nodetree, traversed):
93     if nodetree in traversed:
94         return
95     traversed.add(nodetree)
96
97     for node in nodetree.nodes:
98         if node.bl_idname == 'ShaderNodeGroup':
99             displacement_node_insert(material, node.node_tree, traversed)
100
101     # Gather links to replace
102     displacement_links = []
103     for link in nodetree.links:
104         if link.to_node.bl_idname == 'ShaderNodeOutputMaterial' and \
105            link.from_node.bl_idname != 'ShaderNodeDisplacement' and \
106            link.to_socket.identifier == 'Displacement':
107            displacement_links.append(link)
108
109     # Replace links with displacement node
110     for link in displacement_links:
111         from_node = link.from_node
112         from_socket = link.from_socket
113         to_node = link.to_node
114         to_socket = link.to_socket
115
116         nodetree.links.remove(link)
117
118         node = nodetree.nodes.new(type='ShaderNodeDisplacement')
119         node.location[0] = 0.5 * (from_node.location[0] + to_node.location[0]);
120         node.location[1] = 0.5 * (from_node.location[1] + to_node.location[1]);
121         node.inputs['Scale'].default_value = 0.1
122
123         nodetree.links.new(from_socket, node.inputs['Height'])
124         nodetree.links.new(node.outputs['Displacement'], to_socket)
125
126 def displacement_nodes_insert():
127     traversed = set()
128     for material in bpy.data.materials:
129         if check_is_new_shading_material(material):
130             displacement_node_insert(material, material.node_tree, traversed)
131
132
133 def mapping_node_order_flip(node):
134     """
135     Flip euler order of mapping shader node
136     """
137     if node.bl_idname == 'ShaderNodeMapping':
138         rot = node.rotation.copy()
139         rot.order = 'ZYX'
140         quat = rot.to_quaternion()
141         node.rotation = quat.to_euler('XYZ')
142
143
144 def vector_curve_node_remap(node):
145     """
146     Remap values of vector curve node from normalized to absolute values
147     """
148     if node.bl_idname == 'ShaderNodeVectorCurve':
149         node.mapping.use_clip = False
150         for curve in node.mapping.curves:
151             for point in curve.points:
152                 point.location.x = (point.location.x * 2.0) - 1.0
153                 point.location.y = (point.location.y - 0.5) * 2.0
154         node.mapping.update()
155
156
157 def custom_bake_remap(scene):
158     """
159     Remap bake types into the new types and set the flags accordingly
160     """
161     bake_lookup = (
162         'COMBINED',
163         'AO',
164         'SHADOW',
165         'NORMAL',
166         'UV',
167         'EMIT',
168         'ENVIRONMENT',
169         'DIFFUSE_DIRECT',
170         'DIFFUSE_INDIRECT',
171         'DIFFUSE_COLOR',
172         'GLOSSY_DIRECT',
173         'GLOSSY_INDIRECT',
174         'GLOSSY_COLOR',
175         'TRANSMISSION_DIRECT',
176         'TRANSMISSION_INDIRECT',
177         'TRANSMISSION_COLOR',
178         'SUBSURFACE_DIRECT',
179         'SUBSURFACE_INDIRECT',
180         'SUBSURFACE_COLOR')
181
182     diffuse_direct_idx = bake_lookup.index('DIFFUSE_DIRECT')
183
184     cscene = scene.cycles
185
186     # Old bake type
187     bake_type_idx = cscene.get("bake_type")
188
189     if bake_type_idx is None:
190         cscene.bake_type = 'COMBINED'
191         return
192
193     # File doesn't need versioning
194     if bake_type_idx < diffuse_direct_idx:
195         return
196
197     # File needs versioning
198     bake_type = bake_lookup[bake_type_idx]
199     cscene.bake_type, end = bake_type.split('_')
200
201     if end == 'DIRECT':
202         scene.render.bake.use_pass_indirect = False
203         scene.render.bake.use_pass_color = False
204
205     elif end == 'INDIRECT':
206         scene.render.bake.use_pass_direct = False
207         scene.render.bake.use_pass_color = False
208
209     elif end == 'COLOR':
210         scene.render.bake.use_pass_direct = False
211         scene.render.bake.use_pass_indirect = False
212
213
214 @persistent
215 def do_versions(self):
216     if bpy.context.user_preferences.version <= (2, 78, 1):
217         prop = bpy.context.user_preferences.addons[__package__].preferences
218         system = bpy.context.user_preferences.system
219         if not prop.is_property_set("compute_device_type"):
220             # Device might not currently be available so this can fail
221             try:
222                 if system.legacy_compute_device_type == 1:
223                     prop.compute_device_type = 'OPENCL'
224                 elif system.legacy_compute_device_type == 2:
225                     prop.compute_device_type = 'CUDA'
226                 else:
227                     prop.compute_device_type = 'NONE'
228             except:
229                 pass
230
231             # Init device list for UI
232             prop.get_devices()
233
234     # We don't modify startup file because it assumes to
235     # have all the default values only.
236     if not bpy.data.is_saved:
237         return
238
239     # Clamp Direct/Indirect separation in 270
240     if bpy.data.version <= (2, 70, 0):
241         for scene in bpy.data.scenes:
242             cscene = scene.cycles
243             sample_clamp = cscene.get("sample_clamp", False)
244             if (sample_clamp and
245                 not cscene.is_property_set("sample_clamp_direct") and
246                 not cscene.is_property_set("sample_clamp_indirect")):
247
248                 cscene.sample_clamp_direct = sample_clamp
249                 cscene.sample_clamp_indirect = sample_clamp
250
251     # Change of Volume Bounces in 271
252     if bpy.data.version <= (2, 71, 0):
253         for scene in bpy.data.scenes:
254             cscene = scene.cycles
255             if not cscene.is_property_set("volume_bounces"):
256                 cscene.volume_bounces = 1
257
258     # Caustics Reflective/Refractive separation in 272
259     if bpy.data.version <= (2, 72, 0):
260         for scene in bpy.data.scenes:
261             cscene = scene.cycles
262             if (cscene.get("no_caustics", False) and
263                 not cscene.is_property_set("caustics_reflective") and
264                 not cscene.is_property_set("caustics_refractive")):
265
266                 cscene.caustics_reflective = False
267                 cscene.caustics_refractive = False
268
269     # Euler order was ZYX in previous versions.
270     if bpy.data.version <= (2, 73, 4):
271         foreach_cycles_node(mapping_node_order_flip)
272
273     if bpy.data.version <= (2, 76, 5):
274         foreach_cycles_node(vector_curve_node_remap)
275
276     # Baking types changed
277     if bpy.data.version <= (2, 76, 6):
278         for scene in bpy.data.scenes:
279             custom_bake_remap(scene)
280
281     # Several default changes for 2.77
282     if bpy.data.version <= (2, 76, 8):
283         for scene in bpy.data.scenes:
284             cscene = scene.cycles
285
286             # Samples
287             if not cscene.is_property_set("samples"):
288                 cscene.samples = 10
289
290             # Preview Samples
291             if not cscene.is_property_set("preview_samples"):
292                 cscene.preview_samples = 10
293
294             # Filter
295             if not cscene.is_property_set("filter_type"):
296                 cscene.pixel_filter_type = 'GAUSSIAN'
297
298             # Tile Order
299             if not cscene.is_property_set("tile_order"):
300                 cscene.tile_order = 'CENTER'
301
302         for lamp in bpy.data.lamps:
303             clamp = lamp.cycles
304
305             # MIS
306             if not clamp.is_property_set("use_multiple_importance_sampling"):
307                 clamp.use_multiple_importance_sampling = False
308
309         for mat in bpy.data.materials:
310             cmat = mat.cycles
311
312             # Volume Sampling
313             if not cmat.is_property_set("volume_sampling"):
314                 cmat.volume_sampling = 'DISTANCE'
315
316     if bpy.data.version <= (2, 76, 9):
317         for world in bpy.data.worlds:
318             cworld = world.cycles
319
320             # World MIS
321             if not cworld.is_property_set("sample_as_light"):
322                 cworld.sample_as_light = False
323
324             # World MIS Samples
325             if not cworld.is_property_set("samples"):
326                 cworld.samples = 4
327
328             # World MIS Resolution
329             if not cworld.is_property_set("sample_map_resolution"):
330                 cworld.sample_map_resolution = 256
331
332     if bpy.data.version <= (2, 76, 10):
333         for scene in bpy.data.scenes:
334             cscene = scene.cycles
335             if cscene.is_property_set("filter_type"):
336                 if not cscene.is_property_set("pixel_filter_type"):
337                     cscene.pixel_filter_type = cscene.filter_type
338                 if cscene.filter_type == 'BLACKMAN_HARRIS':
339                     cscene.filter_type = 'GAUSSIAN'
340
341     if bpy.data.version <= (2, 78, 2):
342         for scene in bpy.data.scenes:
343             cscene = scene.cycles
344             if not cscene.is_property_set("light_sampling_threshold"):
345                 cscene.light_sampling_threshold = 0.0
346
347     if bpy.data.version <= (2, 79, 0):
348         for scene in bpy.data.scenes:
349             cscene = scene.cycles
350             # Default changes
351             if not cscene.is_property_set("aa_samples"):
352                 cscene.aa_samples = 4
353             if not cscene.is_property_set("preview_aa_samples"):
354                 cscene.preview_aa_samples = 4
355             if not cscene.is_property_set("blur_glossy"):
356                 cscene.blur_glossy = 0.0
357             if not cscene.is_property_set("sample_clamp_indirect"):
358                 cscene.sample_clamp_indirect = 0.0
359
360     if bpy.data.version <= (2, 79, 1):
361         displacement_nodes_insert()
362
363     if bpy.data.version <= (2, 79, 2):
364         for mat in bpy.data.materials:
365             cmat = mat.cycles
366             if not cmat.is_property_set("displacement_method"):
367                 cmat.displacement_method = 'BUMP'