Files
test/scripts/startup/bl_ui/properties_freestyle.py
Sergey Sharybin 03806d0b67 Re-design of submodules used in blender.git
This commit implements described in the #104573.

The goal is to fix the confusion of the submodule hashes change, which are not
ideal for any of the supported git-module configuration (they are either always
visible causing confusion, or silently staged and committed, also causing
confusion).

This commit replaces submodules with a checkout of addons and addons_contrib,
covered by the .gitignore, and locale and developer tools are moved to the
main repository.

This also changes the paths:
- /release/scripts are moved to the /scripts
- /source/tools are moved to the /tools
- /release/datafiles/locale is moved to /locale

This is done to avoid conflicts when using bisect, and also allow buildbot to
automatically "recover" wgen building older or newer branches/patches.

Running `make update` will initialize the local checkout to the changed
repository configuration.

Another aspect of the change is that the make update will support Github style
of remote organization (origin remote pointing to thy fork, upstream remote
pointing to the upstream blender/blender.git).

Pull Request #104755
2023-02-21 16:39:58 +01:00

1285 lines
45 KiB
Python

# 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)
prop = box.operator("scene.freestyle_fill_range_by_selection")
prop.type = 'COLOR'
prop.name = modifier.name
elif modifier.type == 'DISTANCE_FROM_CAMERA':
draw_modifier_color_ramp_common(box, modifier, True)
prop = box.operator("scene.freestyle_fill_range_by_selection")
prop.type = 'COLOR'
prop.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)
prop = box.operator("scene.freestyle_fill_range_by_selection")
prop.type = 'ALPHA'
prop.name = modifier.name
elif modifier.type == 'DISTANCE_FROM_CAMERA':
draw_modifier_curve_common(box, modifier, True, False)
prop = box.operator("scene.freestyle_fill_range_by_selection")
prop.type = 'ALPHA'
prop.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)
prop = box.operator("scene.freestyle_fill_range_by_selection")
prop.type = 'THICKNESS'
prop.name = modifier.name
elif modifier.type == 'DISTANCE_FROM_CAMERA':
draw_modifier_curve_common(box, modifier, True, True)
prop = box.operator("scene.freestyle_fill_range_by_selection")
prop.type = 'THICKNESS'
prop.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)