Files
test2/scripts/startup/bl_ui/properties_freestyle.py
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

1288 lines
46 KiB
Python

# SPDX-FileCopyrightText: 2012-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu, Panel, UIList
# Render properties
class RenderFreestyleButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "render"
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
@classmethod
def poll(cls, context):
scene = context.scene
with_freestyle = bpy.app.build_options.freestyle
return scene and with_freestyle and (context.engine in cls.COMPAT_ENGINES)
class RENDER_PT_freestyle(RenderFreestyleButtonsPanel, Panel):
bl_label = "Freestyle"
bl_options = {'DEFAULT_CLOSED'}
bl_order = 10
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
def draw_header(self, context):
rd = context.scene.render
self.layout.prop(rd, "use_freestyle", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
rd = context.scene.render
layout.active = rd.use_freestyle
layout.row().prop(rd, "line_thickness_mode", expand=True, text="Line Thickness Mode")
if rd.line_thickness_mode == 'ABSOLUTE':
layout.prop(rd, "line_thickness")
# Render layer properties
class ViewLayerFreestyleButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "view_layer"
bl_order = 10
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
@classmethod
def poll(cls, context):
scene = context.scene
rd = scene.render
with_freestyle = bpy.app.build_options.freestyle
return (
scene
and with_freestyle
and rd.use_freestyle
and (context.engine in cls.COMPAT_ENGINES)
)
class ViewLayerFreestyleEditorButtonsPanel(ViewLayerFreestyleButtonsPanel):
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
@classmethod
def poll(cls, context):
if not super().poll(context):
return False
view_layer = context.view_layer
return (
view_layer
and view_layer.use_freestyle
and view_layer.freestyle_settings.mode == 'EDITOR'
)
class ViewLayerFreestyleLineStyle(ViewLayerFreestyleEditorButtonsPanel):
# Freestyle Linestyle Panels
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
@classmethod
def poll(cls, context):
if not super().poll(context):
return False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
if lineset is None:
return False
linestyle = lineset.linestyle
if linestyle is None:
return False
return True
class ViewLayerFreestyleLinestyleStrokesSubPanel(ViewLayerFreestyleLineStyle):
# Freestyle Linestyle Strokes sub panels
bl_parent_id = "VIEWLAYER_PT_freestyle_linestyle_strokes"
class VIEWLAYER_UL_linesets(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, index):
lineset = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.prop(lineset, "name", text="", emboss=False, icon_value=icon)
layout.prop(lineset, "show_render", text="", index=index)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)
class RENDER_MT_lineset_context_menu(Menu):
bl_label = "Lineset Specials"
def draw(self, _context):
layout = self.layout
layout.operator("scene.freestyle_lineset_copy", icon='COPYDOWN')
layout.operator("scene.freestyle_lineset_paste", icon='PASTEDOWN')
class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel):
bl_label = "Freestyle"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
def draw_header(self, context):
view_layer = context.view_layer
rd = context.scene.render
layout = self.layout
layout.active = rd.use_freestyle
layout.prop(view_layer, "use_freestyle", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
layout.active = view_layer.use_freestyle
col = layout.column(align=True)
col.prop(freestyle, "mode", text="Control Mode")
col.prop(freestyle, "use_view_map_cache", text="View Map Cache")
col.prop(freestyle, "as_render_pass", text="As Render Pass")
class VIEWLAYER_PT_freestyle_edge_detection(ViewLayerFreestyleButtonsPanel, Panel):
bl_label = "Edge Detection"
bl_parent_id = "VIEWLAYER_PT_freestyle"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
layout.active = view_layer.use_freestyle
col = layout.column()
col.prop(freestyle, "crease_angle")
col.prop(freestyle, "use_culling")
col.prop(freestyle, "use_smoothness")
if freestyle.mode == 'SCRIPT':
col.prop(freestyle, "use_material_boundaries")
if freestyle.mode == 'SCRIPT':
col.prop(freestyle, "use_ridges_and_valleys")
col.prop(freestyle, "use_suggestive_contours")
col.prop(freestyle, "sphere_radius")
col.prop(freestyle, "kr_derivative_epsilon")
class VIEWLAYER_PT_freestyle_style_modules(ViewLayerFreestyleButtonsPanel, Panel):
bl_label = "Style Modules"
bl_parent_id = "VIEWLAYER_PT_freestyle"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
@classmethod
def poll(cls, context):
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
return freestyle.mode == 'SCRIPT'
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
layout.active = view_layer.use_freestyle
col = layout.column()
col.use_property_split = False
row = col.row()
row.operator("scene.freestyle_module_add", text="Add")
for module in freestyle.modules:
box = col.box()
box.context_pointer_set("freestyle_module", module)
row = box.row(align=True)
row.prop(module, "use", text="")
row.prop(module, "script", text="")
row.operator("scene.freestyle_module_open", icon='FILEBROWSER', text="")
row.operator("scene.freestyle_module_remove", icon='X', text="")
row.operator("scene.freestyle_module_move", icon='TRIA_UP', text="").direction = 'UP'
row.operator("scene.freestyle_module_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
class VIEWLAYER_PT_freestyle_lineset(ViewLayerFreestyleEditorButtonsPanel, Panel):
bl_label = "Freestyle Line Set"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
def draw_edge_type_buttons(self, box, lineset, edge_type):
# property names
select_edge_type = "select_" + edge_type
exclude_edge_type = "exclude_" + edge_type
# draw edge type buttons
row = box.row(align=True)
row.prop(lineset, select_edge_type)
sub = row.column(align=True)
sub.prop(lineset, exclude_edge_type, text="")
sub.active = getattr(lineset, select_edge_type)
def draw(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
row = layout.row()
is_sortable = len(freestyle.linesets) > 1
rows = 3
if is_sortable:
rows = 5
row.template_list(
"VIEWLAYER_UL_linesets",
"",
freestyle,
"linesets",
freestyle.linesets,
"active_index",
rows=rows,
)
col = row.column(align=True)
col.operator("scene.freestyle_lineset_add", icon='ADD', text="")
col.operator("scene.freestyle_lineset_remove", icon='REMOVE', text="")
col.separator()
col.menu("RENDER_MT_lineset_context_menu", icon='DOWNARROW_HLT', text="")
if is_sortable:
col.separator()
col.operator("scene.freestyle_lineset_move", icon='TRIA_UP', text="").direction = 'UP'
col.operator("scene.freestyle_lineset_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
if lineset:
layout.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
layout.separator()
col = layout.column(heading="Select by")
col.use_property_split = True
col.use_property_decorate = False
col.prop(lineset, "select_by_image_border", text="Image Border")
# Freestyle Lineset Sub Panels
class VIEWLAYER_PT_freestyle_lineset_visibilty(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Visibility"
bl_parent_id = "VIEWLAYER_PT_freestyle_lineset"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.prop(lineset, "select_by_visibility", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.active = lineset.select_by_visibility
col = layout.column(align=True, heading="Type")
col.prop(lineset, "visibility", text="Type", expand=False)
if lineset.visibility == 'RANGE':
col = layout.column(align=True)
col.use_property_split = True
col.prop(lineset, "qi_start")
col.prop(lineset, "qi_end")
class VIEWLAYER_PT_freestyle_lineset_edgetype(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Edge Type"
bl_parent_id = "VIEWLAYER_PT_freestyle_lineset"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.prop(lineset, "select_by_edge_types", text="")
def draw_edge_type_buttons(self, box, lineset, edge_type):
# property names
select_edge_type = "select_" + edge_type
exclude_edge_type = "exclude_" + edge_type
# draw edge type buttons
row = box.row(align=True)
row.prop(lineset, select_edge_type)
sub = row.column(align=True)
sub.prop(lineset, exclude_edge_type, text="")
sub.active = getattr(lineset, select_edge_type)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.active = lineset.select_by_edge_types
layout.row().prop(lineset, "edge_type_negation", expand=True, text="Negation")
layout.row().prop(lineset, "edge_type_combination", expand=True, text="Combination")
col = layout.column(heading="Type")
self.draw_edge_type_buttons(col, lineset, "silhouette")
self.draw_edge_type_buttons(col, lineset, "crease")
self.draw_edge_type_buttons(col, lineset, "border")
self.draw_edge_type_buttons(col, lineset, "edge_mark")
self.draw_edge_type_buttons(col, lineset, "contour")
self.draw_edge_type_buttons(col, lineset, "external_contour")
self.draw_edge_type_buttons(col, lineset, "material_boundary")
self.draw_edge_type_buttons(col, lineset, "suggestive_contour")
self.draw_edge_type_buttons(col, lineset, "ridge_valley")
class VIEWLAYER_PT_freestyle_lineset_facemarks(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Face Marks"
bl_parent_id = "VIEWLAYER_PT_freestyle_lineset"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.prop(lineset, "select_by_face_marks", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.active = lineset.select_by_face_marks
layout.row().prop(lineset, "face_mark_negation", expand=True, text="Negation")
layout.row().prop(lineset, "face_mark_condition", expand=True, text="Condition")
class VIEWLAYER_PT_freestyle_lineset_collection(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Collection"
bl_parent_id = "VIEWLAYER_PT_freestyle_lineset"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.prop(lineset, "select_by_collection", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
layout.active = lineset.select_by_collection
layout.row().prop(lineset, "collection", text="Line Set Collection")
layout.row().prop(lineset, "collection_negation", expand=True, text="Negation")
# Linestyle Modifier Drawing code
def draw_modifier_box_header(box, modifier):
row = box.row()
row.use_property_split = False
row.context_pointer_set("modifier", modifier)
if modifier.expanded:
icon = 'TRIA_DOWN'
else:
icon = 'TRIA_RIGHT'
row.prop(modifier, "expanded", text="", icon=icon, emboss=False)
sub = row.row(align=True)
sub.prop(modifier, "name", text="")
if modifier.use:
icon = 'RESTRICT_RENDER_OFF'
else:
icon = 'RESTRICT_RENDER_ON'
sub.prop(modifier, "use", text="", icon=icon)
sub.operator("scene.freestyle_modifier_copy", icon='DUPLICATE', text="")
sub = row.row(align=True)
sub.operator("scene.freestyle_modifier_move", icon='TRIA_UP', text="").direction = 'UP'
sub.operator("scene.freestyle_modifier_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
row.operator("scene.freestyle_modifier_remove", icon='X', text="", emboss=False)
def draw_modifier_box_error(box, _modifier, message):
row = box.row()
row.label(text=message, icon='ERROR')
def draw_modifier_common(box, modifier):
col = box.column()
col.prop(modifier, "blend", text="Blend Mode")
col.prop(modifier, "influence")
def draw_modifier_color_ramp_common(box, modifier, has_range):
box.template_color_ramp(modifier, "color_ramp", expand=True)
if has_range:
col = box.column(align=True)
col.prop(modifier, "range_min", text="Range Min")
col.prop(modifier, "range_max", text="Max")
def draw_modifier_curve_common(box, modifier, has_range, has_value):
row = box.row()
row.prop(modifier, "mapping", text="Mapping")
if modifier.mapping == 'LINEAR':
box.prop(modifier, "invert")
if has_range:
col = box.column(align=True)
col.prop(modifier, "range_min", text="Range Min")
col.prop(modifier, "range_max", text="Max")
if has_value:
col = box.column(align=True)
col.prop(modifier, "value_min", text="Value Min")
col.prop(modifier, "value_max", text="Max")
if modifier.mapping == 'CURVE':
box.template_curve_mapping(modifier, "curve")
class VIEWLAYER_PT_freestyle_linestyle_strokes(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Freestyle Strokes"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
layout.active = view_layer.use_freestyle
if lineset is None:
return
linestyle = lineset.linestyle
if linestyle is None:
return
row = layout.row(align=True)
row.alignment = 'LEFT'
row.label(text=lineset.name, icon='LINE_DATA')
row.label(text="", icon='RIGHTARROW')
row.label(text=linestyle.name)
col = layout.column(align=True)
col.prop(linestyle, "caps", expand=False)
class VIEWLAYER_PT_freestyle_linestyle_strokes_chaining(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel):
bl_label = "Chaining"
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
linestyle = lineset.linestyle
layout.prop(linestyle, "use_chaining", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
linestyle = lineset.linestyle
layout.active = linestyle.use_chaining
layout.row().prop(linestyle, "chaining", expand=True, text="Method")
if linestyle.chaining == 'SKETCHY':
layout.prop(linestyle, "rounds")
layout.prop(linestyle, "use_same_object")
class VIEWLAYER_PT_freestyle_linestyle_strokes_splitting(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel):
bl_label = "Splitting"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
linestyle = lineset.linestyle
row = layout.row(align=False, heading="Min 2D Angle")
row.prop(linestyle, "use_angle_min", text="")
sub = row.row()
sub.active = linestyle.use_angle_min
sub.prop(linestyle, "angle_min", text="")
row = layout.row(align=False, heading="Max 2D Angle")
row.prop(linestyle, "use_angle_max", text="")
sub = row.row()
sub.active = linestyle.use_angle_max
sub.prop(linestyle, "angle_max", text="")
row = layout.row(align=False, heading="2D Length")
row.prop(linestyle, "use_split_length", text="")
sub = row.row()
sub.active = linestyle.use_split_length
sub.prop(linestyle, "split_length", text="")
layout.prop(linestyle, "material_boundary")
class VIEWLAYER_PT_freestyle_linestyle_strokes_splitting_pattern(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel):
bl_label = "Split Pattern"
bl_parent_id = "VIEWLAYER_PT_freestyle_linestyle_strokes_splitting"
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
linestyle = lineset.linestyle
layout.prop(linestyle, "use_split_pattern", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
linestyle = lineset.linestyle
layout.active = linestyle.use_split_pattern
col = layout.column(align=True)
col.prop(linestyle, "split_dash1", text="Dash 1")
col.prop(linestyle, "split_dash2", text="2")
col.prop(linestyle, "split_dash3", text="3")
col = layout.column(align=True)
col.prop(linestyle, "split_gap1", text="Gap 1")
col.prop(linestyle, "split_gap2", text="2")
col.prop(linestyle, "split_gap3", text="3")
class VIEWLAYER_PT_freestyle_linestyle_strokes_sorting(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel):
bl_label = "Sorting"
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
linestyle = lineset.linestyle
layout.prop(linestyle, "use_sorting", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
linestyle = lineset.linestyle
layout.active = linestyle.use_sorting
layout.prop(linestyle, "sort_key")
row = layout.row()
row.active = linestyle.sort_key in {
'DISTANCE_FROM_CAMERA',
'PROJECTED_X',
'PROJECTED_Y',
}
row.prop(linestyle, "integration_type")
layout.row().prop(linestyle, "sort_order", expand=True, text="Sort Order")
class VIEWLAYER_PT_freestyle_linestyle_strokes_selection(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel):
bl_label = "Selection"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
linestyle = lineset.linestyle
row = layout.row(align=False, heading="Min 2D Length")
row.prop(linestyle, "use_length_min", text="")
sub = row.row()
sub.active = linestyle.use_length_min
sub.prop(linestyle, "length_min", text="")
row = layout.row(align=False, heading="Max 2D Length")
row.prop(linestyle, "use_length_max", text="")
sub = row.row()
sub.active = linestyle.use_length_max
sub.prop(linestyle, "length_max", text="")
row = layout.row(align=False, heading="Chain Count")
row.prop(linestyle, "use_chain_count", text="")
sub = row.row()
sub.active = linestyle.use_chain_count
sub.prop(linestyle, "chain_count", text="")
class VIEWLAYER_PT_freestyle_linestyle_strokes_dashedline(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel):
bl_label = "Dashed Line"
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
layout = self.layout
view_layer = context.view_layer
freestyle = view_layer.freestyle_settings
lineset = freestyle.linesets.active
linestyle = lineset.linestyle
layout.prop(linestyle, "use_dashed_line", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
linestyle = lineset.linestyle
layout.active = linestyle.use_dashed_line
col = layout.column(align=True)
col.prop(linestyle, "dash1", text="Dash 1")
col.prop(linestyle, "dash2", text="2")
col.prop(linestyle, "dash3", text="3")
col = layout.column(align=True)
col.prop(linestyle, "gap1", text="Gap 1")
col.prop(linestyle, "gap2", text="2")
col.prop(linestyle, "gap3", text="3")
class VIEWLAYER_PT_freestyle_linestyle_color(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Freestyle Color"
bl_options = {'DEFAULT_CLOSED'}
def draw_color_modifier(self, context, modifier):
layout = self.layout
col = layout.column(align=True)
draw_modifier_box_header(col.box(), modifier)
if modifier.expanded:
box = col.box()
draw_modifier_common(box, modifier)
if modifier.type == 'ALONG_STROKE':
draw_modifier_color_ramp_common(box, modifier, False)
elif modifier.type == 'DISTANCE_FROM_OBJECT':
box.prop(modifier, "target")
draw_modifier_color_ramp_common(box, modifier, True)
props = box.operator("scene.freestyle_fill_range_by_selection")
props.type = 'COLOR'
props.name = modifier.name
elif modifier.type == 'DISTANCE_FROM_CAMERA':
draw_modifier_color_ramp_common(box, modifier, True)
props = box.operator("scene.freestyle_fill_range_by_selection")
props.type = 'COLOR'
props.name = modifier.name
elif modifier.type == 'MATERIAL':
row = box.row()
row.prop(modifier, "material_attribute",
text="Material Attribute")
sub = box.column()
sub.prop(modifier, "use_ramp")
if modifier.material_attribute in {'LINE', 'DIFF', 'SPEC'}:
sub.active = True
show_ramp = modifier.use_ramp
else:
sub.active = False
show_ramp = True
if show_ramp:
draw_modifier_color_ramp_common(box, modifier, False)
elif modifier.type == 'TANGENT':
draw_modifier_color_ramp_common(box, modifier, False)
elif modifier.type == 'NOISE':
subcol = box.column(align=True)
subcol.prop(modifier, "amplitude")
subcol.prop(modifier, "period")
subcol.prop(modifier, "seed")
draw_modifier_color_ramp_common(box, modifier, False)
elif modifier.type == 'CREASE_ANGLE':
subcol = box.column(align=True)
subcol.prop(modifier, "angle_min", text="Angle Min")
subcol.prop(modifier, "angle_max", text="Max")
draw_modifier_color_ramp_common(box, modifier, False)
elif modifier.type == 'CURVATURE_3D':
subcol = box.column(align=True)
subcol.prop(modifier, "curvature_min", text="Curvature Min")
subcol.prop(modifier, "curvature_max", text="Max")
draw_modifier_color_ramp_common(box, modifier, False)
freestyle = context.view_layer.freestyle_settings
if not freestyle.use_smoothness:
message = "Enable Face Smoothness to use this modifier"
draw_modifier_box_error(col.box(), modifier, message)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
layout.active = view_layer.use_freestyle
if lineset is None:
return
linestyle = lineset.linestyle
if linestyle is None:
return
row = layout.row(align=True)
row.alignment = 'LEFT'
row.label(text=lineset.name, icon='LINE_DATA')
row.label(text="", icon='RIGHTARROW')
row.label(text=linestyle.name)
col = layout.column()
row = col.row()
row.prop(linestyle, "color", text="Base Color")
col.operator_menu_enum("scene.freestyle_color_modifier_add", "type", text="Add Modifier")
for modifier in linestyle.color_modifiers:
self.draw_color_modifier(context, modifier)
class VIEWLAYER_PT_freestyle_linestyle_alpha(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Freestyle Alpha"
bl_options = {'DEFAULT_CLOSED'}
def draw_alpha_modifier(self, context, modifier):
layout = self.layout
col = layout.column(align=True)
draw_modifier_box_header(col.box(), modifier)
if modifier.expanded:
box = col.box()
draw_modifier_common(box, modifier)
if modifier.type == 'ALONG_STROKE':
draw_modifier_curve_common(box, modifier, False, False)
elif modifier.type == 'DISTANCE_FROM_OBJECT':
box.prop(modifier, "target")
draw_modifier_curve_common(box, modifier, True, False)
props = box.operator("scene.freestyle_fill_range_by_selection")
props.type = 'ALPHA'
props.name = modifier.name
elif modifier.type == 'DISTANCE_FROM_CAMERA':
draw_modifier_curve_common(box, modifier, True, False)
props = box.operator("scene.freestyle_fill_range_by_selection")
props.type = 'ALPHA'
props.name = modifier.name
elif modifier.type == 'MATERIAL':
box.prop(modifier, "material_attribute", text="Material Attribute")
draw_modifier_curve_common(box, modifier, False, False)
elif modifier.type == 'TANGENT':
draw_modifier_curve_common(box, modifier, False, False)
elif modifier.type == 'NOISE':
col = box.column(align=True)
col.prop(modifier, "amplitude")
col.prop(modifier, "period")
col.prop(modifier, "seed")
draw_modifier_curve_common(box, modifier, False, False)
elif modifier.type == 'CREASE_ANGLE':
col = box.column(align=True)
col.prop(modifier, "angle_min", text="Angle Min")
col.prop(modifier, "angle_max", text="Max")
draw_modifier_curve_common(box, modifier, False, False)
elif modifier.type == 'CURVATURE_3D':
draw_modifier_curve_common(box, modifier, False, False)
subcol = box.column(align=True)
subcol.prop(modifier, "curvature_min", text="Curvature Min")
subcol.prop(modifier, "curvature_max", text="Max")
freestyle = context.view_layer.freestyle_settings
if not freestyle.use_smoothness:
message = "Enable Face Smoothness to use this modifier"
draw_modifier_box_error(col.box(), modifier, message)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
layout.active = view_layer.use_freestyle
if lineset is None:
return
linestyle = lineset.linestyle
if linestyle is None:
return
row = layout.row(align=True)
row.alignment = 'LEFT'
row.label(text=lineset.name, icon='LINE_DATA')
row.label(text="", icon='RIGHTARROW')
row.label(text=linestyle.name)
col = layout.column()
row = col.row()
row.prop(linestyle, "alpha", text="Base Transparency")
col.operator_menu_enum("scene.freestyle_alpha_modifier_add", "type", text="Add Modifier")
for modifier in linestyle.alpha_modifiers:
self.draw_alpha_modifier(context, modifier)
class VIEWLAYER_PT_freestyle_linestyle_thickness(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Freestyle Thickness"
bl_options = {'DEFAULT_CLOSED'}
def draw_thickness_modifier(self, context, modifier):
layout = self.layout
col = layout.column(align=True)
draw_modifier_box_header(col.box(), modifier)
if modifier.expanded:
box = col.box()
draw_modifier_common(box, modifier)
if modifier.type == 'ALONG_STROKE':
draw_modifier_curve_common(box, modifier, False, True)
elif modifier.type == 'DISTANCE_FROM_OBJECT':
box.prop(modifier, "target")
draw_modifier_curve_common(box, modifier, True, True)
props = box.operator("scene.freestyle_fill_range_by_selection")
props.type = 'THICKNESS'
props.name = modifier.name
elif modifier.type == 'DISTANCE_FROM_CAMERA':
draw_modifier_curve_common(box, modifier, True, True)
props = box.operator("scene.freestyle_fill_range_by_selection")
props.type = 'THICKNESS'
props.name = modifier.name
elif modifier.type == 'MATERIAL':
box.prop(modifier, "material_attribute", text="Material Attribute")
draw_modifier_curve_common(box, modifier, False, True)
elif modifier.type == 'CALLIGRAPHY':
box.prop(modifier, "orientation")
subcol = box.column(align=True)
subcol.prop(modifier, "thickness_min", text="Thickness Min")
subcol.prop(modifier, "thickness_max", text="Max")
elif modifier.type == 'TANGENT':
self.mapping = 'CURVE'
subcol = box.column(align=True)
subcol.prop(modifier, "thickness_min", text="Thickness Min")
subcol.prop(modifier, "thickness_max", text="Max")
draw_modifier_curve_common(box, modifier, False, False)
elif modifier.type == 'NOISE':
col = box.column(align=True)
col.prop(modifier, "amplitude")
col.prop(modifier, "period")
col = box.column(align=True)
col.prop(modifier, "seed")
col.prop(modifier, "use_asymmetric")
elif modifier.type == 'CREASE_ANGLE':
col = box.column(align=True)
col.prop(modifier, "thickness_min", text="Thickness Min")
col.prop(modifier, "thickness_max", text="Max")
col = box.column(align=True)
col.prop(modifier, "angle_min", text="Angle Min")
col.prop(modifier, "angle_max", text="Max")
draw_modifier_curve_common(box, modifier, False, False)
elif modifier.type == 'CURVATURE_3D':
subcol = box.column(align=True)
subcol.prop(modifier, "thickness_min", text="Thickness Min")
subcol.prop(modifier, "thickness_max", text="Max")
subcol = box.column(align=True)
subcol.prop(modifier, "curvature_min")
subcol.prop(modifier, "curvature_max")
draw_modifier_curve_common(box, modifier, False, False)
freestyle = context.view_layer.freestyle_settings
if not freestyle.use_smoothness:
message = "Enable Face Smoothness to use this modifier"
draw_modifier_box_error(col.box(), modifier, message)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
layout.active = view_layer.use_freestyle
if lineset is None:
return
linestyle = lineset.linestyle
if linestyle is None:
return
row = layout.row(align=True)
row.alignment = 'LEFT'
row.label(text=lineset.name, icon='LINE_DATA')
row.label(text="", icon='RIGHTARROW')
row.label(text=linestyle.name)
col = layout.column()
row = col.row()
row.prop(linestyle, "thickness", text="Base Thickness")
subcol = col.column()
subcol.active = linestyle.chaining == 'PLAIN' and linestyle.use_same_object
row = subcol.row()
row.prop(linestyle, "thickness_position", expand=False)
if linestyle.thickness_position == 'RELATIVE':
row = subcol.row()
row.prop(linestyle, "thickness_ratio")
col = layout.column()
col.operator_menu_enum("scene.freestyle_thickness_modifier_add", "type", text="Add Modifier")
for modifier in linestyle.thickness_modifiers:
self.draw_thickness_modifier(context, modifier)
class VIEWLAYER_PT_freestyle_linestyle_geometry(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Freestyle Geometry"
bl_options = {'DEFAULT_CLOSED'}
def draw_geometry_modifier(self, _context, modifier):
layout = self.layout
col = layout.column(align=True)
draw_modifier_box_header(col.box(), modifier)
if modifier.expanded:
box = col.box()
if modifier.type == 'SAMPLING':
box.prop(modifier, "sampling")
elif modifier.type == 'BEZIER_CURVE':
box.prop(modifier, "error")
elif modifier.type == 'SINUS_DISPLACEMENT':
col = box.column(align=True)
col.prop(modifier, "wavelength")
col.prop(modifier, "amplitude")
col = box.column(align=True)
col.prop(modifier, "phase")
elif modifier.type == 'SPATIAL_NOISE':
col = box.column(align=True)
col.prop(modifier, "amplitude")
col.prop(modifier, "scale")
col.prop(modifier, "octaves")
col = box.column(align=True)
col.prop(modifier, "smooth")
col.prop(modifier, "use_pure_random")
elif modifier.type == 'PERLIN_NOISE_1D':
col = box.column(align=True)
col.prop(modifier, "frequency")
col.prop(modifier, "amplitude")
col.prop(modifier, "seed")
col = box.column(align=True)
col.prop(modifier, "octaves")
col.prop(modifier, "angle")
elif modifier.type == 'PERLIN_NOISE_2D':
col = box.column(align=True)
col.prop(modifier, "frequency")
col.prop(modifier, "amplitude")
col.prop(modifier, "seed")
col = box.column(align=True)
col.prop(modifier, "octaves")
col.prop(modifier, "angle")
elif modifier.type == 'BACKBONE_STRETCHER':
box.prop(modifier, "backbone_length")
elif modifier.type == 'TIP_REMOVER':
box.prop(modifier, "tip_length")
elif modifier.type == 'POLYGONIZATION':
box.prop(modifier, "error")
elif modifier.type == 'GUIDING_LINES':
box.prop(modifier, "offset")
elif modifier.type == 'BLUEPRINT':
row = box.row()
row.prop(modifier, "shape", expand=True)
box.prop(modifier, "rounds")
subcol = box.column(align=True)
if modifier.shape in {'CIRCLES', 'ELLIPSES'}:
subcol.prop(modifier, "random_radius", text="Random Radius")
subcol.prop(modifier, "random_center", text="Center")
elif modifier.shape == 'SQUARES':
subcol.prop(modifier, "backbone_length", text="Backbone Length")
subcol.prop(modifier, "random_backbone", text="Randomness")
elif modifier.type == '2D_OFFSET':
subcol = box.column(align=True)
subcol.prop(modifier, "start")
subcol.prop(modifier, "end")
subcol = box.column(align=True)
subcol.prop(modifier, "x")
subcol.prop(modifier, "y")
elif modifier.type == '2D_TRANSFORM':
box.prop(modifier, "pivot")
if modifier.pivot == 'PARAM':
box.prop(modifier, "pivot_u")
elif modifier.pivot == 'ABSOLUTE':
subcol = box.column(align=True)
subcol.prop(modifier, "pivot_x", text="Pivot X")
subcol.prop(modifier, "pivot_y", text="Y")
subcol = box.column(align=True)
subcol.prop(modifier, "scale_x", text="Scale X")
subcol.prop(modifier, "scale_y", text="Y")
box.prop(modifier, "angle")
elif modifier.type == 'SIMPLIFICATION':
box.prop(modifier, "tolerance")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
layout.active = view_layer.use_freestyle
if lineset is None:
return
linestyle = lineset.linestyle
if linestyle is None:
return
row = layout.row(align=True)
row.alignment = 'LEFT'
row.label(text=lineset.name, icon='LINE_DATA')
row.label(text="", icon='RIGHTARROW')
row.label(text=linestyle.name)
col = layout.column()
col.operator_menu_enum("scene.freestyle_geometry_modifier_add", "type", text="Add Modifier")
for modifier in linestyle.geometry_modifiers:
self.draw_geometry_modifier(context, modifier)
class VIEWLAYER_PT_freestyle_linestyle_texture(ViewLayerFreestyleLineStyle, Panel):
bl_label = "Freestyle Texture"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
view_layer = context.view_layer
lineset = view_layer.freestyle_settings.linesets.active
layout.active = view_layer.use_freestyle
if lineset is None:
return
linestyle = lineset.linestyle
if linestyle is None:
return
row = layout.row(align=True)
row.alignment = 'LEFT'
row.label(text=lineset.name, icon='LINE_DATA')
row.label(text="", icon='RIGHTARROW')
row.label(text=linestyle.name)
layout.prop(linestyle, "use_nodes")
layout.prop(linestyle, "texture_spacing", text="Spacing Along Stroke")
row = layout.row()
props = row.operator(
"wm.properties_context_change",
text="Go to Linestyle Textures Properties",
icon='TEXTURE',
)
props.context = 'TEXTURE'
# Material properties
class MaterialFreestyleButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "material"
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
@classmethod
def poll(cls, context):
scene = context.scene
material = context.material
with_freestyle = bpy.app.build_options.freestyle
return (
with_freestyle
and material
and scene
and scene.render.use_freestyle
and (context.engine in cls.COMPAT_ENGINES)
)
class MATERIAL_PT_freestyle_line(MaterialFreestyleButtonsPanel, Panel):
bl_label = "Freestyle Line"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'BLENDER_WORKBENCH_NEXT'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
mat = context.material
col = layout.column()
col.prop(mat, "line_color")
col.prop(mat, "line_priority", text="Priority")
classes = (
RENDER_PT_freestyle,
VIEWLAYER_UL_linesets,
RENDER_MT_lineset_context_menu,
VIEWLAYER_PT_freestyle,
VIEWLAYER_PT_freestyle_edge_detection,
VIEWLAYER_PT_freestyle_style_modules,
VIEWLAYER_PT_freestyle_lineset,
VIEWLAYER_PT_freestyle_lineset_visibilty,
VIEWLAYER_PT_freestyle_lineset_edgetype,
VIEWLAYER_PT_freestyle_lineset_facemarks,
VIEWLAYER_PT_freestyle_lineset_collection,
VIEWLAYER_PT_freestyle_linestyle_strokes,
VIEWLAYER_PT_freestyle_linestyle_strokes_chaining,
VIEWLAYER_PT_freestyle_linestyle_strokes_splitting,
VIEWLAYER_PT_freestyle_linestyle_strokes_splitting_pattern,
VIEWLAYER_PT_freestyle_linestyle_strokes_sorting,
VIEWLAYER_PT_freestyle_linestyle_strokes_selection,
VIEWLAYER_PT_freestyle_linestyle_strokes_dashedline,
VIEWLAYER_PT_freestyle_linestyle_color,
VIEWLAYER_PT_freestyle_linestyle_alpha,
VIEWLAYER_PT_freestyle_linestyle_thickness,
VIEWLAYER_PT_freestyle_linestyle_geometry,
VIEWLAYER_PT_freestyle_linestyle_texture,
MATERIAL_PT_freestyle_line,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)