Cycles: integrator presets, patch by Thomas.
[blender.git] / intern / cycles / blender / addon / ui.py
1 #
2 # Copyright 2011, Blender Foundation.
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 #
18
19 import bpy
20
21 from bpy.types import Panel, Menu
22
23 from cycles import enums
24 from cycles import engine
25
26 class CYCLES_MT_integrator_presets(Menu):
27     bl_label = "Integrator Presets"
28     preset_subdir = "cycles/integrator"
29     preset_operator = "script.execute_preset"
30     COMPAT_ENGINES = {'CYCLES'}
31     draw = Menu.draw_preset
32
33 class CyclesButtonsPanel():
34     bl_space_type = "PROPERTIES"
35     bl_region_type = "WINDOW"
36     bl_context = "render"
37     
38     @classmethod
39     def poll(cls, context):
40         rd = context.scene.render
41         return rd.engine == 'CYCLES'
42
43 class CyclesRender_PT_integrator(CyclesButtonsPanel, Panel):
44     bl_label = "Integrator"
45
46     def draw(self, context):
47         layout = self.layout
48
49         scene = context.scene
50         cscene = scene.cycles
51         
52         row = layout.row(align=True)
53         row.menu("CYCLES_MT_integrator_presets", text=bpy.types.CYCLES_MT_integrator_presets.bl_label)
54         row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMIN")
55         row.operator("render.cycles_integrator_preset_add", text="", icon="ZOOMOUT").remove_active = True
56
57         split = layout.split()
58
59         col = split.column()
60         sub = col.column(align=True)
61         sub.prop(cscene, "passes", text="Render Passes")
62         sub.prop(cscene, "preview_passes")
63         col.prop(cscene, "no_caustics")
64
65         col = split.column()
66         sub = col.column(align=True)
67         sub.prop(cscene, "max_bounces")
68         sub.prop(cscene, "min_bounces")
69
70         #row = col.row()
71         #row.prop(cscene, "blur_caustics")
72         #row.active = not cscene.no_caustics
73         
74 class CyclesRender_PT_film(CyclesButtonsPanel, Panel):
75     bl_label = "Film"
76
77     def draw(self, context):
78         layout = self.layout
79
80         scene = context.scene
81         cscene = scene.cycles
82
83         split = layout.split()
84
85         col = split.column();
86         col.prop(cscene, "exposure")
87         col.prop(cscene, "transparent")
88
89         col = split.column()
90         sub = col.column(align=True)
91         sub.prop(cscene, "filter_type", text="")
92         if cscene.filter_type != 'BOX':
93             sub.prop(cscene, "filter_width", text="Width")
94
95 class CyclesRender_PT_performance(CyclesButtonsPanel, Panel):
96     bl_label = "Performance"
97     bl_options = {'DEFAULT_CLOSED'}
98
99     def draw(self, context):
100         layout = self.layout
101
102         scene = context.scene
103         rd = scene.render
104         cscene = scene.cycles
105
106         split = layout.split()
107
108         col = split.column(align=True)
109
110         col.label(text="Threads:")
111         col.row().prop(rd, "threads_mode", expand=True)
112         sub = col.column()
113         sub.enabled = rd.threads_mode == 'FIXED'
114         sub.prop(rd, "threads")
115
116         sub = col.column(align=True)
117         sub.label(text="Tiles:")
118         sub.prop(cscene, "debug_tile_size")
119         sub.prop(cscene, "debug_min_size")
120
121         col = split.column()
122
123         sub = col.column(align=True)
124         sub.label(text="Acceleration structure:")
125         sub.prop(cscene, "debug_bvh_type", text="")
126         sub.prop(cscene, "debug_use_spatial_splits")
127
128 class Cycles_PT_post_processing(CyclesButtonsPanel, Panel):
129     bl_label = "Post Processing"
130     bl_options = {'DEFAULT_CLOSED'}
131
132     def draw(self, context):
133         layout = self.layout
134
135         rd = context.scene.render
136
137         split = layout.split()
138
139         col = split.column()
140         col.prop(rd, "use_compositing")
141         col.prop(rd, "use_sequencer")
142
143         col = split.column()
144         col.prop(rd, "dither_intensity", text="Dither", slider=True)
145
146 class Cycles_PT_camera(CyclesButtonsPanel, Panel):
147     bl_label = "Cycles"
148     bl_context = "data"
149
150     @classmethod
151     def poll(cls, context):
152         return context.camera
153
154     def draw(self, context):
155         layout = self.layout
156
157         camera = context.camera
158         ccamera = camera.cycles
159
160         layout.prop(ccamera, "lens_radius")
161
162 class Cycles_PT_context_material(CyclesButtonsPanel, Panel):
163     bl_label = "Surface"
164     bl_context = "material"
165     bl_options = {'HIDE_HEADER'}
166
167     @classmethod
168     def poll(cls, context):
169         return (context.material or context.object) and CyclesButtonsPanel.poll(context)
170
171     def draw(self, context):
172         layout = self.layout
173
174         mat = context.material
175         ob = context.object
176         slot = context.material_slot
177         space = context.space_data
178
179         if ob:
180             row = layout.row()
181
182             row.template_list(ob, "material_slots", ob, "active_material_index", rows=2)
183
184             col = row.column(align=True)
185             col.operator("object.material_slot_add", icon='ZOOMIN', text="")
186             col.operator("object.material_slot_remove", icon='ZOOMOUT', text="")
187
188             col.menu("MATERIAL_MT_specials", icon='DOWNARROW_HLT', text="")
189
190             if ob.mode == 'EDIT':
191                 row = layout.row(align=True)
192                 row.operator("object.material_slot_assign", text="Assign")
193                 row.operator("object.material_slot_select", text="Select")
194                 row.operator("object.material_slot_deselect", text="Deselect")
195
196         split = layout.split(percentage=0.65)
197
198         if ob:
199             split.template_ID(ob, "active_material", new="material.new")
200             row = split.row()
201
202             if slot:
203                 row.prop(slot, "link", text="")
204             else:
205                 row.label()
206         elif mat:
207             split.template_ID(space, "pin_id")
208             split.separator()
209
210 class Cycles_PT_mesh_displacement(CyclesButtonsPanel, Panel):
211     bl_label = "Displacement"
212     bl_context = "data"
213
214     @classmethod
215     def poll(cls, context):
216         return context.mesh or context.curve or context.meta_ball
217
218     def draw(self, context):
219         layout = self.layout
220
221         mesh = context.mesh
222         curve = context.curve
223         mball = context.meta_ball
224
225         if mesh:
226             cdata = mesh.cycles
227         elif curve:
228             cdata = curve.cycles
229         elif mball:
230             cdata = mball.cycles
231
232         layout.prop(cdata, "displacement_method", text="Method")
233         layout.prop(cdata, "use_subdivision");
234         layout.prop(cdata, "dicing_rate");
235
236 def find_node(material, nodetype):
237     if material and material.node_tree:
238         ntree = material.node_tree
239
240         for node in ntree.nodes:
241             if type(node) is not bpy.types.NodeGroup and node.type == nodetype:
242                 return node
243     
244     return None
245
246 def find_node_input(node, name):
247     for input in node.inputs:
248         if input.name == name:
249             return input
250     
251     return None
252
253 def panel_node_draw(layout, id, output_type, input_name):
254     if not id.node_tree:
255         layout.prop(id, "use_nodes")
256         return
257
258     ntree = id.node_tree
259
260     node = find_node(id, output_type)
261     if not node:
262         layout.label(text="No output node.")
263     else:
264         input = find_node_input(node, input_name)
265         layout.template_node_view(ntree, node, input);
266
267 class CyclesLamp_PT_lamp(CyclesButtonsPanel, Panel):
268     bl_label = "Surface"
269     bl_context = "data"
270
271     @classmethod
272     def poll(cls, context):
273         return context.lamp and CyclesButtonsPanel.poll(context)
274
275     def draw(self, context):
276         layout = self.layout
277
278         mat = context.lamp
279         panel_node_draw(layout, mat, 'OUTPUT_LAMP', 'Surface')
280
281 class CyclesWorld_PT_surface(CyclesButtonsPanel, Panel):
282     bl_label = "Surface"
283     bl_context = "world"
284
285     @classmethod
286     def poll(cls, context):
287         return context.world and CyclesButtonsPanel.poll(context)
288
289     def draw(self, context):
290         layout = self.layout
291
292         mat = context.world
293         panel_node_draw(layout, mat, 'OUTPUT_WORLD', 'Surface')
294
295 class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel):
296     bl_label = "Volume"
297     bl_context = "world"
298
299     @classmethod
300     def poll(cls, context):
301         return context.world and CyclesButtonsPanel.poll(context)
302
303     def draw(self, context):
304         layout = self.layout
305         layout.active = False
306
307         mat = context.world
308         panel_node_draw(layout, mat, 'OUTPUT_WORLD', 'Volume')
309
310 class CyclesMaterial_PT_surface(CyclesButtonsPanel, Panel):
311     bl_label = "Surface"
312     bl_context = "material"
313
314     @classmethod
315     def poll(cls, context):
316         return context.material and CyclesButtonsPanel.poll(context)
317
318     def draw(self, context):
319         layout = self.layout
320
321         mat = context.material
322         panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Surface')
323
324 class CyclesMaterial_PT_volume(CyclesButtonsPanel, Panel):
325     bl_label = "Volume"
326     bl_context = "material"
327
328     @classmethod
329     def poll(cls, context):
330         return context.material and CyclesButtonsPanel.poll(context)
331
332     def draw(self, context):
333         layout = self.layout
334         layout.active = False
335
336         mat = context.material
337         panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Volume')
338
339 class CyclesMaterial_PT_displacement(CyclesButtonsPanel, Panel):
340     bl_label = "Displacement"
341     bl_context = "material"
342
343     @classmethod
344     def poll(cls, context):
345         return context.material and CyclesButtonsPanel.poll(context)
346
347     def draw(self, context):
348         layout = self.layout
349
350         mat = context.material
351         panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Displacement')
352
353 class CyclesMaterial_PT_settings(CyclesButtonsPanel, Panel):
354     bl_label = "Settings"
355     bl_context = "material"
356     bl_options = {'DEFAULT_CLOSED'}
357
358     @classmethod
359     def poll(cls, context):
360         # return context.material and CyclesButtonsPanel.poll(context)
361         return False
362
363     def draw(self, context):
364         layout = self.layout
365
366         mat = context.material
367     
368         row = layout.row()
369         row.label(text="Light Group:")
370         row.prop(mat, "light_group", text="")
371
372 class CyclesTexture_PT_context(CyclesButtonsPanel, Panel):
373     bl_label = ""
374     bl_context = "texture"
375     bl_options = {'HIDE_HEADER'}
376     COMPAT_ENGINES = {'CYCLES'}
377
378     def draw(self, context):
379         layout = self.layout
380
381         tex = context.texture
382         space = context.space_data
383         pin_id = space.pin_id
384         use_pin_id = space.use_pin_id;
385         user = context.texture_user
386         node = context.texture_node
387
388         if not use_pin_id or not isinstance(pin_id, bpy.types.Texture):
389             pin_id = None
390
391         if not pin_id:
392             layout.template_texture_user()
393
394         if user:
395             layout.separator()
396
397             split = layout.split(percentage=0.65)
398             col = split.column()
399
400             if pin_id:
401                 col.template_ID(space, "pin_id")
402             elif user:
403                 col.template_ID(user, "texture", new="texture.new")
404             
405             if tex:
406                 row = split.row()
407                 row.prop(tex, "use_nodes", icon="NODETREE", text="")
408                 row.label()
409
410                 if not tex.use_nodes:
411                     split = layout.split(percentage=0.2)
412                     split.label(text="Type:")
413                     split.prop(tex, "type", text="")
414
415 class CyclesTexture_PT_nodes(CyclesButtonsPanel, Panel):
416     bl_label = "Nodes"
417     bl_context = "texture"
418
419     @classmethod
420     def poll(cls, context):
421         tex = context.texture
422         return (tex and tex.use_nodes) and CyclesButtonsPanel.poll(context)
423
424     def draw(self, context):
425         layout = self.layout
426
427         tex = context.texture
428         panel_node_draw(layout, tex, 'OUTPUT_TEXTURE', 'Color')
429
430 class CyclesTexture_PT_node(CyclesButtonsPanel, Panel):
431     bl_label = "Node"
432     bl_context = "texture"
433
434     @classmethod
435     def poll(cls, context):
436         node = context.texture_node
437         return node and CyclesButtonsPanel.poll(context)
438
439     def draw(self, context):
440         layout = self.layout
441
442         node = context.texture_node
443         ntree = node.id_data
444         layout.template_node_view(ntree, node, None)
445
446 class CyclesTexture_PT_mapping(CyclesButtonsPanel, Panel):
447     bl_label = "Mapping"
448     bl_context = "texture"
449
450     @classmethod
451     def poll(cls, context):
452         tex = context.texture
453         node = context.texture_node
454         return (node or (tex and tex.use_nodes)) and CyclesButtonsPanel.poll(context)
455
456     def draw(self, context):
457         layout = self.layout
458         layout.label("Texture coordinate mapping goes here.");
459         layout.label("Translate, rotate, scale, projection, XYZ.")
460
461 class CyclesTexture_PT_color(CyclesButtonsPanel, Panel):
462     bl_label = "Color"
463     bl_context = "texture"
464
465     @classmethod
466     def poll(cls, context):
467         tex = context.texture
468         node = context.texture_node
469         return (node or (tex and tex.use_nodes)) and CyclesButtonsPanel.poll(context)
470
471     def draw(self, context):
472         layout = self.layout
473         layout.label("Color modification options go here.");
474         layout.label("Ramp, brightness, contrast, saturation.")
475     
476 def draw_device(self, context):
477     scene = context.scene
478     layout = self.layout
479
480     if scene.render.engine == "CYCLES":
481         cscene = scene.cycles
482
483         if 'cuda' in engine.available_devices():
484             layout.prop(cscene, "device")
485         if cscene.device == 'CPU' and engine.with_osl():
486             layout.prop(cscene, "shading_system")
487
488 def draw_pause(self, context):
489     layout = self.layout
490     scene = context.scene
491
492     if scene.render.engine == "CYCLES":
493         view = context.space_data
494
495         if view.viewport_shade == "RENDERED":
496             cscene = scene.cycles
497             layout.prop(cscene, "preview_pause", icon="PAUSE", text="")
498
499 def get_panels():
500     return [
501         bpy.types.RENDER_PT_render,
502         bpy.types.RENDER_PT_output,
503         bpy.types.RENDER_PT_encoding,
504         bpy.types.RENDER_PT_dimensions,
505         bpy.types.RENDER_PT_stamp,
506         bpy.types.WORLD_PT_context_world,
507         bpy.types.DATA_PT_context_mesh,
508         bpy.types.DATA_PT_context_camera,
509         bpy.types.DATA_PT_context_lamp,
510         bpy.types.DATA_PT_texture_space,
511         bpy.types.DATA_PT_curve_texture_space,
512         bpy.types.DATA_PT_mball_texture_space,
513         bpy.types.DATA_PT_vertex_groups,
514         bpy.types.DATA_PT_shape_keys,
515         bpy.types.DATA_PT_uv_texture,
516         bpy.types.DATA_PT_vertex_colors,
517         bpy.types.DATA_PT_camera,
518         bpy.types.DATA_PT_camera_display,
519         bpy.types.DATA_PT_custom_props_mesh,
520         bpy.types.DATA_PT_custom_props_camera,
521         bpy.types.DATA_PT_custom_props_lamp,
522         bpy.types.TEXTURE_PT_clouds,
523         bpy.types.TEXTURE_PT_wood,
524         bpy.types.TEXTURE_PT_marble,
525         bpy.types.TEXTURE_PT_magic,
526         bpy.types.TEXTURE_PT_blend,
527         bpy.types.TEXTURE_PT_stucci,
528         bpy.types.TEXTURE_PT_image,
529         bpy.types.TEXTURE_PT_image_sampling,
530         bpy.types.TEXTURE_PT_image_mapping,
531         bpy.types.TEXTURE_PT_musgrave,
532         bpy.types.TEXTURE_PT_voronoi,
533         bpy.types.TEXTURE_PT_distortednoise,
534         bpy.types.TEXTURE_PT_voxeldata,
535         bpy.types.TEXTURE_PT_pointdensity,
536         bpy.types.TEXTURE_PT_pointdensity_turbulence]
537
538 def register():
539     bpy.types.RENDER_PT_render.append(draw_device)
540     bpy.types.VIEW3D_HT_header.append(draw_pause)
541
542     for panel in get_panels():
543         panel.COMPAT_ENGINES.add('CYCLES')
544     
545 def unregister():
546     bpy.types.RENDER_PT_render.remove(draw_device)
547     bpy.types.VIEW3D_HT_header.remove(draw_pause)
548
549     for panel in get_panels():
550         panel.COMPAT_ENGINES.remove('CYCLES')
551