Merge branch 'blender2.7'
[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 import math
21
22 from bpy.app.handlers import persistent
23
24
25 def foreach_cycles_nodetree_group(nodetree, traversed):
26     for node in nodetree.nodes:
27         if node.bl_idname == 'ShaderNodeGroup':
28             group = node.node_tree
29             if group and group not in traversed:
30                 traversed.add(group)
31                 yield group, group.library
32                 yield from foreach_cycles_nodetree_group(group, traversed)
33
34
35 def foreach_cycles_nodetree():
36     traversed = set()
37
38     for material in bpy.data.materials:
39         nodetree = material.node_tree
40         if nodetree:
41             yield nodetree, material.library
42             yield from foreach_cycles_nodetree_group(nodetree, traversed)
43
44     for world in bpy.data.worlds:
45         nodetree = world.node_tree
46         if nodetree:
47             yield nodetree, world.library
48             foreach_cycles_nodetree_group(nodetree, traversed)
49
50     for light in bpy.data.lights:
51         nodetree = light.node_tree
52         if nodetree:
53             yield nodetree, light.library
54             foreach_cycles_nodetree_group(nodetree, traversed)
55
56
57 def displacement_node_insert(nodetree):
58     # Gather links to replace
59     displacement_links = []
60     for link in nodetree.links:
61         if (
62                 link.to_node.bl_idname == 'ShaderNodeOutputMaterial' and
63                 link.from_node.bl_idname != 'ShaderNodeDisplacement' and
64                 link.to_socket.identifier == 'Displacement'
65         ):
66             displacement_links.append(link)
67
68     # Replace links with displacement node
69     for link in displacement_links:
70         from_node = link.from_node
71         from_socket = link.from_socket
72         to_node = link.to_node
73         to_socket = link.to_socket
74
75         nodetree.links.remove(link)
76
77         node = nodetree.nodes.new(type='ShaderNodeDisplacement')
78         node.location[0] = 0.5 * (from_node.location[0] + to_node.location[0])
79         node.location[1] = 0.5 * (from_node.location[1] + to_node.location[1])
80         node.inputs['Scale'].default_value = 0.1
81         node.inputs['Midlevel'].default_value = 0.0
82
83         nodetree.links.new(from_socket, node.inputs['Height'])
84         nodetree.links.new(node.outputs['Displacement'], to_socket)
85
86
87 def displacement_principled_nodes(node):
88     if node.bl_idname == 'ShaderNodeDisplacement':
89         if node.space != 'WORLD':
90             node.space = 'OBJECT'
91     if node.bl_idname == 'ShaderNodeBsdfPrincipled':
92         if node.subsurface_method != 'RANDOM_WALK':
93             node.subsurface_method = 'BURLEY'
94
95
96 def square_roughness_node_insert(nodetree):
97     roughness_node_types = {
98         'ShaderNodeBsdfAnisotropic',
99         'ShaderNodeBsdfGlass',
100         'ShaderNodeBsdfGlossy',
101         'ShaderNodeBsdfRefraction'}
102
103     # Update default values
104     for node in nodetree.nodes:
105         if node.bl_idname in roughness_node_types:
106             roughness_input = node.inputs['Roughness']
107             roughness_input.default_value = math.sqrt(max(roughness_input.default_value, 0.0))
108
109     # Gather roughness links to replace
110     roughness_links = []
111     for link in nodetree.links:
112         if link.to_node.bl_idname in roughness_node_types and \
113            link.to_socket.identifier == 'Roughness':
114             roughness_links.append(link)
115
116     # Replace links with sqrt node
117     for link in roughness_links:
118         from_node = link.from_node
119         from_socket = link.from_socket
120         to_node = link.to_node
121         to_socket = link.to_socket
122
123         nodetree.links.remove(link)
124
125         node = nodetree.nodes.new(type='ShaderNodeMath')
126         node.operation = 'POWER'
127         node.location[0] = 0.5 * (from_node.location[0] + to_node.location[0])
128         node.location[1] = 0.5 * (from_node.location[1] + to_node.location[1])
129
130         nodetree.links.new(from_socket, node.inputs[0])
131         node.inputs[1].default_value = 0.5
132         nodetree.links.new(node.outputs['Value'], to_socket)
133
134
135 def mapping_node_order_flip(node):
136     """
137     Flip euler order of mapping shader node
138     """
139     if node.bl_idname == 'ShaderNodeMapping':
140         rot = node.rotation.copy()
141         rot.order = 'ZYX'
142         quat = rot.to_quaternion()
143         node.rotation = quat.to_euler('XYZ')
144
145
146 def vector_curve_node_remap(node):
147     """
148     Remap values of vector curve node from normalized to absolute values
149     """
150     if node.bl_idname == 'ShaderNodeVectorCurve':
151         node.mapping.use_clip = False
152         for curve in node.mapping.curves:
153             for point in curve.points:
154                 point.location.x = (point.location.x * 2.0) - 1.0
155                 point.location.y = (point.location.y - 0.5) * 2.0
156         node.mapping.update()
157
158
159 def custom_bake_remap(scene):
160     """
161     Remap bake types into the new types and set the flags accordingly
162     """
163     bake_lookup = (
164         'COMBINED',
165         'AO',
166         'SHADOW',
167         'NORMAL',
168         'UV',
169         'EMIT',
170         'ENVIRONMENT',
171         'DIFFUSE_DIRECT',
172         'DIFFUSE_INDIRECT',
173         'DIFFUSE_COLOR',
174         'GLOSSY_DIRECT',
175         'GLOSSY_INDIRECT',
176         'GLOSSY_COLOR',
177         'TRANSMISSION_DIRECT',
178         'TRANSMISSION_INDIRECT',
179         'TRANSMISSION_COLOR',
180         'SUBSURFACE_DIRECT',
181         'SUBSURFACE_INDIRECT',
182         'SUBSURFACE_COLOR')
183
184     diffuse_direct_idx = bake_lookup.index('DIFFUSE_DIRECT')
185
186     cscene = scene.cycles
187
188     # Old bake type
189     bake_type_idx = cscene.get("bake_type")
190
191     if bake_type_idx is None:
192         cscene.bake_type = 'COMBINED'
193         return
194
195     # File doesn't need versioning
196     if bake_type_idx < diffuse_direct_idx:
197         return
198
199     # File needs versioning
200     bake_type = bake_lookup[bake_type_idx]
201     cscene.bake_type, end = bake_type.split('_')
202
203     if end == 'DIRECT':
204         scene.render.bake.use_pass_indirect = False
205         scene.render.bake.use_pass_color = False
206
207     elif end == 'INDIRECT':
208         scene.render.bake.use_pass_direct = False
209         scene.render.bake.use_pass_color = False
210
211     elif end == 'COLOR':
212         scene.render.bake.use_pass_direct = False
213         scene.render.bake.use_pass_indirect = False
214
215
216 def ambient_occlusion_node_relink(nodetree):
217     for node in nodetree.nodes:
218         if node.bl_idname == 'ShaderNodeAmbientOcclusion':
219             node.samples = 1
220             node.only_local = False
221             node.inputs['Distance'].default_value = 0.0
222
223     # Gather links to replace
224     ao_links = []
225     for link in nodetree.links:
226         if link.from_node.bl_idname == 'ShaderNodeAmbientOcclusion':
227             ao_links.append(link)
228
229     # Replace links
230     for link in ao_links:
231         from_node = link.from_node
232         to_socket = link.to_socket
233
234         nodetree.links.remove(link)
235         nodetree.links.new(from_node.outputs['Color'], to_socket)
236
237
238 @persistent
239 def do_versions(self):
240     if bpy.context.preferences.version <= (2, 78, 1):
241         prop = bpy.context.preferences.addons[__package__].preferences
242         system = bpy.context.preferences.system
243         if not prop.is_property_set("compute_device_type"):
244             # Device might not currently be available so this can fail
245             try:
246                 if system.legacy_compute_device_type == 1:
247                     prop.compute_device_type = 'OPENCL'
248                 elif system.legacy_compute_device_type == 2:
249                     prop.compute_device_type = 'CUDA'
250                 else:
251                     prop.compute_device_type = 'NONE'
252             except:
253                 pass
254
255             # Init device list for UI
256             prop.get_devices(prop.compute_device_type)
257
258     # We don't modify startup file because it assumes to
259     # have all the default values only.
260     if not bpy.data.is_saved:
261         return
262
263     # Map of versions used by libraries.
264     library_versions = {}
265     library_versions[bpy.data.version] = [None]
266     for library in bpy.data.libraries:
267         library_versions.setdefault(library.version, []).append(library)
268
269     # Do versioning per library, since they might have different versions.
270     max_need_versioning = (2, 79, 6)
271     for version, libraries in library_versions.items():
272         if version > max_need_versioning:
273             continue
274
275         # Scenes
276         for scene in bpy.data.scenes:
277             if scene.library not in libraries:
278                 continue
279
280             # Clamp Direct/Indirect separation in 270
281             if version <= (2, 70, 0):
282                 cscene = scene.cycles
283                 sample_clamp = cscene.get("sample_clamp", False)
284                 if (sample_clamp and
285                     not cscene.is_property_set("sample_clamp_direct") and
286                         not cscene.is_property_set("sample_clamp_indirect")):
287                     cscene.sample_clamp_direct = sample_clamp
288                     cscene.sample_clamp_indirect = sample_clamp
289
290             # Change of Volume Bounces in 271
291             if version <= (2, 71, 0):
292                 cscene = scene.cycles
293                 if not cscene.is_property_set("volume_bounces"):
294                     cscene.volume_bounces = 1
295
296             # Caustics Reflective/Refractive separation in 272
297             if version <= (2, 72, 0):
298                 cscene = scene.cycles
299                 if (cscene.get("no_caustics", False) and
300                     not cscene.is_property_set("caustics_reflective") and
301                     not cscene.is_property_set("caustics_refractive")):
302                     cscene.caustics_reflective = False
303                     cscene.caustics_refractive = False
304
305             # Baking types changed
306             if version <= (2, 76, 6):
307                 custom_bake_remap(scene)
308
309             # Several default changes for 2.77
310             if version <= (2, 76, 8):
311                 cscene = scene.cycles
312
313                 # Samples
314                 if not cscene.is_property_set("samples"):
315                     cscene.samples = 10
316
317                 # Preview Samples
318                 if not cscene.is_property_set("preview_samples"):
319                     cscene.preview_samples = 10
320
321                 # Filter
322                 if not cscene.is_property_set("filter_type"):
323                     cscene.pixel_filter_type = 'GAUSSIAN'
324
325                 # Tile Order
326                 if not cscene.is_property_set("tile_order"):
327                     cscene.tile_order = 'CENTER'
328
329             if version <= (2, 76, 10):
330                 cscene = scene.cycles
331                 if cscene.is_property_set("filter_type"):
332                     if not cscene.is_property_set("pixel_filter_type"):
333                         cscene.pixel_filter_type = cscene.filter_type
334                     if cscene.filter_type == 'BLACKMAN_HARRIS':
335                         cscene.filter_type = 'GAUSSIAN'
336
337             if version <= (2, 78, 2):
338                 cscene = scene.cycles
339                 if not cscene.is_property_set("light_sampling_threshold"):
340                     cscene.light_sampling_threshold = 0.0
341
342             if version <= (2, 79, 0):
343                 cscene = scene.cycles
344                 # Default changes
345                 if not cscene.is_property_set("aa_samples"):
346                     cscene.aa_samples = 4
347                 if not cscene.is_property_set("preview_aa_samples"):
348                     cscene.preview_aa_samples = 4
349                 if not cscene.is_property_set("blur_glossy"):
350                     cscene.blur_glossy = 0.0
351                 if not cscene.is_property_set("sample_clamp_indirect"):
352                     cscene.sample_clamp_indirect = 0.0
353
354         # Lamps
355         for light in bpy.data.lights:
356             if light.library not in libraries:
357                 continue
358
359             if version <= (2, 76, 5):
360                 clight = light.cycles
361
362                 # MIS
363                 if not clight.is_property_set("use_multiple_importance_sampling"):
364                     clight.use_multiple_importance_sampling = False
365
366         # Worlds
367         for world in bpy.data.worlds:
368             if world.library not in libraries:
369                 continue
370
371             if version <= (2, 76, 9):
372                 cworld = world.cycles
373
374                 # World MIS Samples
375                 if not cworld.is_property_set("samples"):
376                     cworld.samples = 4
377
378                 # World MIS Resolution
379                 if not cworld.is_property_set("sample_map_resolution"):
380                     cworld.sample_map_resolution = 256
381
382             if version <= (2, 79, 4) or \
383                (version >= (2, 80, 0) and version <= (2, 80, 18)):
384                 cworld = world.cycles
385                 # World MIS
386                 if not cworld.is_property_set("sampling_method"):
387                     if cworld.get("sample_as_light", True):
388                         cworld.sampling_method = 'MANUAL'
389                     else:
390                         cworld.sampling_method = 'NONE'
391
392         # Materials
393         for mat in bpy.data.materials:
394             if mat.library not in libraries:
395                 continue
396
397             if version <= (2, 76, 5):
398                 cmat = mat.cycles
399                 # Volume Sampling
400                 if not cmat.is_property_set("volume_sampling"):
401                     cmat.volume_sampling = 'DISTANCE'
402
403             if version <= (2, 79, 2):
404                 cmat = mat.cycles
405                 if not cmat.is_property_set("displacement_method"):
406                     cmat.displacement_method = 'BUMP'
407
408             # Change default to bump again.
409             if version <= (2, 79, 6) or \
410                (version >= (2, 80, 0) and version <= (2, 80, 41)):
411                 cmat = mat.cycles
412                 if not cmat.is_property_set("displacement_method"):
413                     cmat.displacement_method = 'DISPLACEMENT'
414
415         # Nodes
416         for nodetree, library in foreach_cycles_nodetree():
417             if library not in libraries:
418                 continue
419
420             # Euler order was ZYX in previous versions.
421             if version <= (2, 73, 4):
422                 for node in nodetree.nodes:
423                     mapping_node_order_flip(node)
424
425             if version <= (2, 76, 5):
426                 for node in nodetree.nodes:
427                     vector_curve_node_remap(node)
428
429             if version <= (2, 79, 1) or \
430                (version >= (2, 80, 0) and version <= (2, 80, 3)):
431                 displacement_node_insert(nodetree)
432
433             if version <= (2, 79, 2):
434                 for node in nodetree.nodes:
435                     displacement_principled_nodes(node)
436
437             if version <= (2, 79, 3) or \
438                (version >= (2, 80, 0) and version <= (2, 80, 4)):
439                 # Switch to squared roughness convention
440                 square_roughness_node_insert(nodetree)
441
442             if version <= (2, 79, 4):
443                 ambient_occlusion_node_relink(nodetree)
444
445         # Particles
446         for part in bpy.data.particles:
447             if part.library not in libraries:
448                 continue
449
450             # Copy cycles hair settings to internal settings
451             if version <= (2, 80, 15):
452                 cpart = part.get("cycles", None)
453                 if cpart:
454                     part.shape = cpart.get("shape", 0.0)
455                     part.root_radius = cpart.get("root_width", 1.0)
456                     part.tip_radius = cpart.get("tip_width", 0.0)
457                     part.radius_scale = cpart.get("radius_scale", 0.01)
458                     part.use_close_tip = cpart.get("use_closetip", True)