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