1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 from bpy.types import Menu, Panel, UIList
26 class RenderFreestyleButtonsPanel():
27 bl_space_type = 'PROPERTIES'
28 bl_region_type = 'WINDOW'
30 # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
33 def poll(cls, context):
35 with_freestyle = bpy.app.build_options.freestyle
36 return scene and with_freestyle and(scene.render.engine in cls.COMPAT_ENGINES)
39 class RENDER_PT_freestyle(RenderFreestyleButtonsPanel, Panel):
40 bl_label = "Freestyle"
41 bl_options = {'DEFAULT_CLOSED'}
42 COMPAT_ENGINES = {'BLENDER_RENDER'}
44 def draw_header(self, context):
45 rd = context.scene.render
46 self.layout.prop(rd, "use_freestyle", text="")
48 def draw(self, context):
51 rd = context.scene.render
53 layout.active = rd.use_freestyle
56 row.label(text="Line Thickness:")
57 row.prop(rd, "line_thickness_mode", expand=True)
59 if (rd.line_thickness_mode == 'ABSOLUTE'):
60 layout.prop(rd, "line_thickness")
63 row.label(text="Line style settings are in the Render Layers tab")
64 row.operator("wm.properties_context_change", text="", icon='BUTS').context = 'RENDER_LAYER'
67 # Render layer properties
69 class RenderLayerFreestyleButtonsPanel():
70 bl_space_type = 'PROPERTIES'
71 bl_region_type = 'WINDOW'
72 bl_context = "render_layer"
73 # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
76 def poll(cls, context):
78 rd = context.scene.render
79 with_freestyle = bpy.app.build_options.freestyle
81 return (scene and with_freestyle and rd.use_freestyle
82 and rd.layers.active and(scene.render.engine in cls.COMPAT_ENGINES))
85 class RenderLayerFreestyleEditorButtonsPanel(RenderLayerFreestyleButtonsPanel):
86 # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
89 def poll(cls, context):
90 if not super().poll(context):
92 rl = context.scene.render.layers.active
93 return rl and rl.freestyle_settings.mode == 'EDITOR'
96 class RENDERLAYER_UL_linesets(UIList):
97 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
99 if self.layout_type in {'DEFAULT', 'COMPACT'}:
100 layout.prop(lineset, "name", text="", emboss=False, icon_value=icon)
101 layout.prop(lineset, "show_render", text="", index=index)
102 elif self.layout_type in {'GRID'}:
103 layout.alignment = 'CENTER'
104 layout.label("", icon_value=icon)
107 class RENDER_MT_lineset_specials(Menu):
108 bl_label = "Lineset Specials"
110 def draw(self, context):
112 layout.operator("scene.freestyle_lineset_copy", icon='COPYDOWN')
113 layout.operator("scene.freestyle_lineset_paste", icon='PASTEDOWN')
116 class RENDERLAYER_PT_freestyle(RenderLayerFreestyleButtonsPanel, Panel):
117 bl_label = "Freestyle"
118 COMPAT_ENGINES = {'BLENDER_RENDER'}
120 def draw(self, context):
123 rd = context.scene.render
124 rl = rd.layers.active
125 freestyle = rl.freestyle_settings
127 layout.active = rl.use_freestyle
129 layout.prop(freestyle, "mode", text="Control mode")
130 layout.label(text="Edge Detection Options:")
132 split = layout.split()
135 col.prop(freestyle, "crease_angle")
136 col.prop(freestyle, "use_culling")
137 col.prop(freestyle, "use_advanced_options")
140 col.prop(freestyle, "use_smoothness")
141 if freestyle.mode == 'SCRIPT':
142 col.prop(freestyle, "use_material_boundaries")
144 # Advanced options are hidden by default to warn new users
145 if freestyle.use_advanced_options:
146 if freestyle.mode == 'SCRIPT':
148 row.prop(freestyle, "use_ridges_and_valleys")
149 row.prop(freestyle, "use_suggestive_contours")
151 row.prop(freestyle, "sphere_radius")
152 row.prop(freestyle, "kr_derivative_epsilon")
154 if freestyle.mode == 'SCRIPT':
156 row.label("Style modules:")
157 row.operator("scene.freestyle_module_add", text="Add")
158 for i, module in enumerate(freestyle.modules):
160 box.context_pointer_set("freestyle_module", module)
161 row = box.row(align=True)
162 row.prop(module, "use", text="")
163 row.prop(module, "script", text="")
164 row.operator("scene.freestyle_module_open", icon='FILESEL', text="")
165 row.operator("scene.freestyle_module_remove", icon='X', text="")
166 row.operator("scene.freestyle_module_move", icon='TRIA_UP', text="").direction = 'UP'
167 row.operator("scene.freestyle_module_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
170 class RENDERLAYER_PT_freestyle_lineset(RenderLayerFreestyleEditorButtonsPanel, Panel):
171 bl_label = "Freestyle Line Set"
172 COMPAT_ENGINES = {'BLENDER_RENDER'}
174 def draw_edge_type_buttons(self, box, lineset, edge_type):
176 select_edge_type = "select_" + edge_type
177 exclude_edge_type = "exclude_" + edge_type
178 # draw edge type buttons
179 row = box.row(align=True)
180 row.prop(lineset, select_edge_type)
181 sub = row.column(align=True)
182 sub.prop(lineset, exclude_edge_type, text="")
183 sub.active = getattr(lineset, select_edge_type)
185 def draw(self, context):
188 rd = context.scene.render
189 rl = rd.layers.active
190 freestyle = rl.freestyle_settings
191 lineset = freestyle.linesets.active
193 layout.active = rl.use_freestyle
196 rows = 4 if lineset else 2
197 row.template_list("RENDERLAYER_UL_linesets", "", freestyle, "linesets", freestyle.linesets, "active_index", rows=rows)
199 sub = row.column(align=True)
200 sub.operator("scene.freestyle_lineset_add", icon='ZOOMIN', text="")
201 sub.operator("scene.freestyle_lineset_remove", icon='ZOOMOUT', text="")
202 sub.menu("RENDER_MT_lineset_specials", icon='DOWNARROW_HLT', text="")
206 sub.operator("scene.freestyle_lineset_move", icon='TRIA_UP', text="").direction = 'UP'
207 sub.operator("scene.freestyle_lineset_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
209 col = layout.column()
210 col.label(text="Selection By:")
211 row = col.row(align=True)
212 row.prop(lineset, "select_by_visibility", text="Visibility", toggle=True)
213 row.prop(lineset, "select_by_edge_types", text="Edge Types", toggle=True)
214 row.prop(lineset, "select_by_face_marks", text="Face Marks", toggle=True)
215 row.prop(lineset, "select_by_group", text="Group", toggle=True)
216 row.prop(lineset, "select_by_image_border", text="Image Border", toggle=True)
218 if lineset.select_by_visibility:
219 col.label(text="Visibility:")
220 row = col.row(align=True)
221 row.prop(lineset, "visibility", expand=True)
222 if lineset.visibility == 'RANGE':
223 row = col.row(align=True)
224 row.prop(lineset, "qi_start")
225 row.prop(lineset, "qi_end")
227 if lineset.select_by_edge_types:
228 col.label(text="Edge Types:")
230 row.prop(lineset, "edge_type_negation", expand=True)
231 row.prop(lineset, "edge_type_combination", expand=True)
236 self.draw_edge_type_buttons(sub, lineset, "silhouette")
237 self.draw_edge_type_buttons(sub, lineset, "border")
238 self.draw_edge_type_buttons(sub, lineset, "contour")
239 self.draw_edge_type_buttons(sub, lineset, "suggestive_contour")
240 self.draw_edge_type_buttons(sub, lineset, "ridge_valley")
243 self.draw_edge_type_buttons(sub, lineset, "crease")
244 self.draw_edge_type_buttons(sub, lineset, "edge_mark")
245 self.draw_edge_type_buttons(sub, lineset, "external_contour")
246 self.draw_edge_type_buttons(sub, lineset, "material_boundary")
248 if lineset.select_by_face_marks:
249 col.label(text="Face Marks:")
251 row.prop(lineset, "face_mark_negation", expand=True)
252 row.prop(lineset, "face_mark_condition", expand=True)
254 if lineset.select_by_group:
255 col.label(text="Group:")
257 row.prop(lineset, "group", text="")
258 row.prop(lineset, "group_negation", expand=True)
261 class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, Panel):
262 bl_label = "Freestyle Line Style"
263 bl_options = {'DEFAULT_CLOSED'}
264 COMPAT_ENGINES = {'BLENDER_RENDER'}
266 def draw_modifier_box_header(self, box, modifier):
268 row.context_pointer_set("modifier", modifier)
269 if modifier.expanded:
273 row.prop(modifier, "expanded", text="", icon=icon, emboss=False)
274 # TODO: Use icons rather than text label, would save some room!
275 row.label(text=modifier.rna_type.name)
276 row.prop(modifier, "name", text="")
278 icon = 'RESTRICT_RENDER_OFF'
280 icon = 'RESTRICT_RENDER_ON'
281 row.prop(modifier, "use", text="", icon=icon)
282 sub = row.row(align=True)
283 sub.operator("scene.freestyle_modifier_copy", icon='NONE', text="Copy")
284 sub.operator("scene.freestyle_modifier_move", icon='TRIA_UP', text="").direction = 'UP'
285 sub.operator("scene.freestyle_modifier_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
286 sub.operator("scene.freestyle_modifier_remove", icon='X', text="")
288 def draw_modifier_common(self, box, modifier):
290 row.prop(modifier, "blend", text="")
291 row.prop(modifier, "influence")
293 def draw_modifier_color_ramp_common(self, box, modifier, has_range):
294 box.template_color_ramp(modifier, "color_ramp", expand=True)
296 row = box.row(align=True)
297 row.prop(modifier, "range_min")
298 row.prop(modifier, "range_max")
300 def draw_modifier_curve_common(self, box, modifier, has_range, has_value):
302 row.prop(modifier, "mapping", text="")
304 sub.prop(modifier, "invert")
305 if modifier.mapping == 'CURVE':
307 box.template_curve_mapping(modifier, "curve")
309 row = box.row(align=True)
310 row.prop(modifier, "range_min")
311 row.prop(modifier, "range_max")
313 row = box.row(align=True)
314 row.prop(modifier, "value_min")
315 row.prop(modifier, "value_max")
317 def draw_color_modifier(self, context, modifier):
320 col = layout.column(align=True)
321 self.draw_modifier_box_header(col.box(), modifier)
322 if modifier.expanded:
324 self.draw_modifier_common(box, modifier)
326 if modifier.type == 'ALONG_STROKE':
327 self.draw_modifier_color_ramp_common(box, modifier, False)
329 elif modifier.type == 'DISTANCE_FROM_OBJECT':
330 box.prop(modifier, "target")
331 self.draw_modifier_color_ramp_common(box, modifier, True)
332 prop = box.operator("scene.freestyle_fill_range_by_selection")
334 prop.name = modifier.name
336 elif modifier.type == 'DISTANCE_FROM_CAMERA':
337 self.draw_modifier_color_ramp_common(box, modifier, True)
338 prop = box.operator("scene.freestyle_fill_range_by_selection")
340 prop.name = modifier.name
342 elif modifier.type == 'MATERIAL':
344 row.prop(modifier, "material_attribute", text="")
346 sub.prop(modifier, "use_ramp")
347 if modifier.material_attribute in {'DIFF', 'SPEC'}:
349 show_ramp = modifier.use_ramp
354 self.draw_modifier_color_ramp_common(box, modifier, False)
356 def draw_alpha_modifier(self, context, modifier):
359 col = layout.column(align=True)
360 self.draw_modifier_box_header(col.box(), modifier)
361 if modifier.expanded:
363 self.draw_modifier_common(box, modifier)
365 if modifier.type == 'ALONG_STROKE':
366 self.draw_modifier_curve_common(box, modifier, False, False)
368 elif modifier.type == 'DISTANCE_FROM_OBJECT':
369 box.prop(modifier, "target")
370 self.draw_modifier_curve_common(box, modifier, True, False)
371 prop = box.operator("scene.freestyle_fill_range_by_selection")
373 prop.name = modifier.name
375 elif modifier.type == 'DISTANCE_FROM_CAMERA':
376 self.draw_modifier_curve_common(box, modifier, True, False)
377 prop = box.operator("scene.freestyle_fill_range_by_selection")
379 prop.name = modifier.name
381 elif modifier.type == 'MATERIAL':
382 box.prop(modifier, "material_attribute", text="")
383 self.draw_modifier_curve_common(box, modifier, False, False)
385 def draw_thickness_modifier(self, context, modifier):
388 col = layout.column(align=True)
389 self.draw_modifier_box_header(col.box(), modifier)
390 if modifier.expanded:
392 self.draw_modifier_common(box, modifier)
394 if modifier.type == 'ALONG_STROKE':
395 self.draw_modifier_curve_common(box, modifier, False, True)
397 elif modifier.type == 'DISTANCE_FROM_OBJECT':
398 box.prop(modifier, "target")
399 self.draw_modifier_curve_common(box, modifier, True, True)
400 prop = box.operator("scene.freestyle_fill_range_by_selection")
401 prop.type = 'THICKNESS'
402 prop.name = modifier.name
404 elif modifier.type == 'DISTANCE_FROM_CAMERA':
405 self.draw_modifier_curve_common(box, modifier, True, True)
406 prop = box.operator("scene.freestyle_fill_range_by_selection")
407 prop.type = 'THICKNESS'
408 prop.name = modifier.name
410 elif modifier.type == 'MATERIAL':
411 box.prop(modifier, "material_attribute", text="")
412 self.draw_modifier_curve_common(box, modifier, False, True)
414 elif modifier.type == 'CALLIGRAPHY':
415 box.prop(modifier, "orientation")
416 row = box.row(align=True)
417 row.prop(modifier, "thickness_min")
418 row.prop(modifier, "thickness_max")
420 def draw_geometry_modifier(self, context, modifier):
423 col = layout.column(align=True)
424 self.draw_modifier_box_header(col.box(), modifier)
425 if modifier.expanded:
428 if modifier.type == 'SAMPLING':
429 box.prop(modifier, "sampling")
431 elif modifier.type == 'BEZIER_CURVE':
432 box.prop(modifier, "error")
434 elif modifier.type == 'SINUS_DISPLACEMENT':
437 col.prop(modifier, "wavelength")
438 col.prop(modifier, "amplitude")
440 col.prop(modifier, "phase")
442 elif modifier.type == 'SPATIAL_NOISE':
445 col.prop(modifier, "amplitude")
446 col.prop(modifier, "scale")
447 col.prop(modifier, "octaves")
449 col.prop(modifier, "smooth")
450 col.prop(modifier, "use_pure_random")
452 elif modifier.type == 'PERLIN_NOISE_1D':
455 col.prop(modifier, "frequency")
456 col.prop(modifier, "amplitude")
457 col.prop(modifier, "seed")
459 col.prop(modifier, "octaves")
460 col.prop(modifier, "angle")
462 elif modifier.type == 'PERLIN_NOISE_2D':
465 col.prop(modifier, "frequency")
466 col.prop(modifier, "amplitude")
467 col.prop(modifier, "seed")
469 col.prop(modifier, "octaves")
470 col.prop(modifier, "angle")
472 elif modifier.type == 'BACKBONE_STRETCHER':
473 box.prop(modifier, "backbone_length")
475 elif modifier.type == 'TIP_REMOVER':
476 box.prop(modifier, "tip_length")
478 elif modifier.type == 'POLYGONIZATION':
479 box.prop(modifier, "error")
481 elif modifier.type == 'GUIDING_LINES':
482 box.prop(modifier, "offset")
484 elif modifier.type == 'BLUEPRINT':
486 row.prop(modifier, "shape", expand=True)
487 box.prop(modifier, "rounds")
489 if modifier.shape in {'CIRCLES', 'ELLIPSES'}:
490 row.prop(modifier, "random_radius")
491 row.prop(modifier, "random_center")
492 elif modifier.shape == 'SQUARES':
493 row.prop(modifier, "backbone_length")
494 row.prop(modifier, "random_backbone")
496 elif modifier.type == '2D_OFFSET':
497 row = box.row(align=True)
498 row.prop(modifier, "start")
499 row.prop(modifier, "end")
500 row = box.row(align=True)
501 row.prop(modifier, "x")
502 row.prop(modifier, "y")
504 elif modifier.type == '2D_TRANSFORM':
505 box.prop(modifier, "pivot")
506 if modifier.pivot == 'PARAM':
507 box.prop(modifier, "pivot_u")
508 elif modifier.pivot == 'ABSOLUTE':
509 row = box.row(align=True)
510 row.prop(modifier, "pivot_x")
511 row.prop(modifier, "pivot_y")
512 row = box.row(align=True)
513 row.prop(modifier, "scale_x")
514 row.prop(modifier, "scale_y")
515 box.prop(modifier, "angle")
517 def draw(self, context):
520 rd = context.scene.render
521 rl = rd.layers.active
522 lineset = rl.freestyle_settings.linesets.active
524 layout.active = rl.use_freestyle
528 linestyle = lineset.linestyle
530 layout.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
531 if linestyle is None:
533 row = layout.row(align=True)
534 row.prop(linestyle, "panel", expand=True)
535 if linestyle.panel == 'STROKES':
537 layout.prop(linestyle, "use_chaining", text="Chaining:")
538 split = layout.split(align=True)
539 split.active = linestyle.use_chaining
542 col.active = linestyle.use_chaining
543 col.prop(linestyle, "chaining", text="")
544 if linestyle.chaining == 'SKETCHY':
545 col.prop(linestyle, "rounds")
548 col.prop(linestyle, "use_same_object")
551 layout.label(text="Splitting:")
552 split = layout.split(align=True)
555 row = col.row(align=True)
556 row.prop(linestyle, "use_angle_min", text="")
558 sub.active = linestyle.use_angle_min
559 sub.prop(linestyle, "angle_min")
560 row = col.row(align=True)
561 row.prop(linestyle, "use_angle_max", text="")
563 sub.active = linestyle.use_angle_max
564 sub.prop(linestyle, "angle_max")
567 row = col.row(align=True)
568 row.prop(linestyle, "use_split_length", text="")
570 sub.active = linestyle.use_split_length
571 sub.prop(linestyle, "split_length", text="2D Length")
572 row = col.row(align=True)
573 row.prop(linestyle, "material_boundary")
575 row = layout.row(align=True)
576 row.prop(linestyle, "use_split_pattern", text="")
577 sub = row.row(align=True)
578 sub.active = linestyle.use_split_pattern
579 sub.prop(linestyle, "split_dash1", text="D1")
580 sub.prop(linestyle, "split_gap1", text="G1")
581 sub.prop(linestyle, "split_dash2", text="D2")
582 sub.prop(linestyle, "split_gap2", text="G2")
583 sub.prop(linestyle, "split_dash3", text="D3")
584 sub.prop(linestyle, "split_gap3", text="G3")
587 layout.label(text="Selection:")
588 split = layout.split(align=True)
591 row = col.row(align=True)
592 row.prop(linestyle, "use_length_min", text="")
594 sub.active = linestyle.use_length_min
595 sub.prop(linestyle, "length_min")
598 row = col.row(align=True)
599 row.prop(linestyle, "use_length_max", text="")
601 sub.active = linestyle.use_length_max
602 sub.prop(linestyle, "length_max")
605 layout.prop(linestyle, "use_sorting", text="Sorting:")
606 col = layout.column()
607 col.active = linestyle.use_sorting
608 row = col.row(align=True)
609 row.prop(linestyle, "sort_key", text="")
611 sub.active = linestyle.sort_key in {'DISTANCE_FROM_CAMERA'}
612 sub.prop(linestyle, "integration_type", text="")
613 row = col.row(align=True)
614 row.prop(linestyle, "sort_order", expand=True)
617 layout.label(text="Caps:")
618 row = layout.row(align=True)
619 row.prop(linestyle, "caps", expand=True)
622 layout.prop(linestyle, "use_dashed_line", text="Dashed Line:")
623 row = layout.row(align=True)
624 row.active = linestyle.use_dashed_line
625 row.prop(linestyle, "dash1", text="D1")
626 row.prop(linestyle, "gap1", text="G1")
627 row.prop(linestyle, "dash2", text="D2")
628 row.prop(linestyle, "gap2", text="G2")
629 row.prop(linestyle, "dash3", text="D3")
630 row.prop(linestyle, "gap3", text="G3")
632 elif linestyle.panel == 'COLOR':
633 col = layout.column()
635 row.label(text="Base Color:")
636 row.prop(linestyle, "color", text="")
637 col.label(text="Modifiers:")
638 col.operator_menu_enum("scene.freestyle_color_modifier_add", "type", text="Add Modifier")
639 for modifier in linestyle.color_modifiers:
640 self.draw_color_modifier(context, modifier)
642 elif linestyle.panel == 'ALPHA':
643 col = layout.column()
645 row.label(text="Base Transparency:")
646 row.prop(linestyle, "alpha")
647 col.label(text="Modifiers:")
648 col.operator_menu_enum("scene.freestyle_alpha_modifier_add", "type", text="Add Modifier")
649 for modifier in linestyle.alpha_modifiers:
650 self.draw_alpha_modifier(context, modifier)
652 elif linestyle.panel == 'THICKNESS':
653 col = layout.column()
655 row.label(text="Base Thickness:")
656 row.prop(linestyle, "thickness")
658 row.prop(linestyle, "thickness_position", expand=True)
660 row.prop(linestyle, "thickness_ratio")
661 row.active = (linestyle.thickness_position == 'RELATIVE')
662 col = layout.column()
663 col.label(text="Modifiers:")
664 col.operator_menu_enum("scene.freestyle_thickness_modifier_add", "type", text="Add Modifier")
665 for modifier in linestyle.thickness_modifiers:
666 self.draw_thickness_modifier(context, modifier)
668 elif linestyle.panel == 'GEOMETRY':
669 col = layout.column()
670 col.label(text="Modifiers:")
671 col.operator_menu_enum("scene.freestyle_geometry_modifier_add", "type", text="Add Modifier")
672 for modifier in linestyle.geometry_modifiers:
673 self.draw_geometry_modifier(context, modifier)
675 elif linestyle.panel == 'MISC':
679 if __name__ == "__main__": # only for live edit.
680 bpy.utils.register_module(__name__)