GPv3: Primitive Tools: Line, Polyline, Arc, Curve, Box and Circle Tool
Adds the primitive tools in draw mode to GPv3. This also adds rotation and scale sub-operators with `r` and `s` keybinds. Also all control points are editable after extruding. Pull Request: https://projects.blender.org/blender/blender/pulls/119039
This commit is contained in:
committed by
Falk David
parent
cf7bd3fe48
commit
2df13ce4a3
@@ -106,6 +106,7 @@ _km_hierarchy = [
|
||||
_km_expand_from_toolsystem('VIEW_3D', 'PARTICLE'),
|
||||
]),
|
||||
|
||||
('Primitive Tool Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Knife Tool Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Custom Normals Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
('Bevel Modal Map', 'EMPTY', 'WINDOW', []),
|
||||
|
||||
@@ -8118,106 +8118,228 @@ def km_3d_view_tool_paint_weight_gradient(params):
|
||||
# ------------------------------------------------------------------------------
|
||||
# Tool System (3D View, Grease Pencil, Paint)
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_line(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Line",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_line", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
|
||||
def km_grease_pencil_primitive_tool_modal_map(params):
|
||||
items = []
|
||||
keymap = (
|
||||
"Primitive Tool Modal Map",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW', "modal": True},
|
||||
{"items": items},
|
||||
)
|
||||
|
||||
items.extend([
|
||||
("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
|
||||
("CANCEL", {"type": 'Q', "value": 'PRESS', "any": True}, None),
|
||||
("PANNING", {"type": 'MIDDLEMOUSE', "value": 'ANY', "any": True}, None),
|
||||
("CONFIRM", {"type": 'RET', "value": 'PRESS', "any": True}, None),
|
||||
("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
|
||||
("EXTRUDE", {"type": 'E', "value": 'PRESS'}, None),
|
||||
("GRAB", {"type": 'G', "value": 'PRESS'}, None),
|
||||
("ROTATE", {"type": 'R', "value": 'PRESS'}, None),
|
||||
("SCALE", {"type": 'S', "value": 'PRESS'}, None),
|
||||
("INCREASE_SUBDIVISION", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None),
|
||||
("DECREASE_SUBDIVISION", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_line(params):
|
||||
if params.use_experimental_grease_pencil_version3:
|
||||
return (
|
||||
"3D View Tool: Paint Grease Pencil, Line",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("grease_pencil.primitive_line", params.tool_maybe_tweak_event,
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": []}),
|
||||
# Lasso select
|
||||
("grease_pencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Line",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_line", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_polyline(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Polyline",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_polyline", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
if params.use_experimental_grease_pencil_version3:
|
||||
return (
|
||||
"3D View Tool: Paint Grease Pencil, Polyline",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("grease_pencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS'},
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": []}),
|
||||
# Lasso select
|
||||
("grease_pencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Polyline",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_polyline", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_box(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Box",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_box", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
if params.use_experimental_grease_pencil_version3:
|
||||
return (
|
||||
"3D View Tool: Paint Grease Pencil, Box",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("grease_pencil.primitive_box", params.tool_maybe_tweak_event,
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": []}),
|
||||
# Lasso select
|
||||
("grease_pencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Box",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_box", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_circle(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Circle",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_circle", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
if params.use_experimental_grease_pencil_version3:
|
||||
return (
|
||||
"3D View Tool: Paint Grease Pencil, Circle",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("grease_pencil.primitive_circle", params.tool_maybe_tweak_event,
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": []}),
|
||||
# Lasso select
|
||||
("grease_pencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Circle",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_circle", params.tool_maybe_tweak_event,
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_arc(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Arc",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_curve", params.tool_maybe_tweak_event,
|
||||
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
|
||||
("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
|
||||
("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
if params.use_experimental_grease_pencil_version3:
|
||||
return (
|
||||
"3D View Tool: Paint Grease Pencil, Arc",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("grease_pencil.primitive_arc", params.tool_maybe_tweak_event,
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_arc", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": []}),
|
||||
("grease_pencil.primitive_arc", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": []}),
|
||||
# Lasso select
|
||||
("grease_pencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Arc",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_curve", params.tool_maybe_tweak_event,
|
||||
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
|
||||
("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
|
||||
("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("type", 'ARC'), ("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_curve(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Curve",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_curve", params.tool_maybe_tweak_event,
|
||||
{"properties": [("type", 'CURVE'), ("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
if params.use_experimental_grease_pencil_version3:
|
||||
return (
|
||||
"3D View Tool: Paint Grease Pencil, Curve",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("grease_pencil.primitive_curve", params.tool_maybe_tweak_event,
|
||||
{"properties": []}),
|
||||
# Lasso select
|
||||
("grease_pencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Curve",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.primitive_curve", params.tool_maybe_tweak_event,
|
||||
{"properties": [("type", 'CURVE'), ("wait_for_input", False)]}),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso",
|
||||
{"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_cutter(params):
|
||||
@@ -8702,6 +8824,7 @@ def generate_keymaps(params=None):
|
||||
km_sculpt_mesh_filter_modal_map(params),
|
||||
km_curve_pen_modal_map(params),
|
||||
km_node_link_modal_map(params),
|
||||
km_grease_pencil_primitive_tool_modal_map(params),
|
||||
|
||||
# Gizmos.
|
||||
km_generic_gizmo(params),
|
||||
|
||||
@@ -1432,6 +1432,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
|
||||
|
||||
def brush_basic_grease_pencil_paint_settings(layout, context, brush, *, compact=False):
|
||||
gp_settings = brush.gpencil_settings
|
||||
tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False)
|
||||
if gp_settings is None:
|
||||
return
|
||||
|
||||
@@ -1468,7 +1469,39 @@ def brush_basic_grease_pencil_paint_settings(layout, context, brush, *, compact=
|
||||
col = layout.column()
|
||||
col.template_curve_mapping(gp_settings, "curve_strength", brush=True, use_negative_slope=True)
|
||||
|
||||
if grease_pencil_tool == 'DRAW':
|
||||
# Brush details
|
||||
if tool.idname in {
|
||||
"builtin.arc",
|
||||
"builtin.curve",
|
||||
"builtin.line",
|
||||
"builtin.box",
|
||||
"builtin.circle",
|
||||
"builtin.polyline",
|
||||
}:
|
||||
row = layout.row(align=True)
|
||||
if context.region.type == 'TOOL_HEADER':
|
||||
row.prop(gp_settings, "caps_type", text="", expand=True)
|
||||
else:
|
||||
row.prop(gp_settings, "caps_type", text="Caps Type")
|
||||
|
||||
settings = context.tool_settings.gpencil_sculpt
|
||||
if compact:
|
||||
row = layout.row(align=True)
|
||||
row.prop(settings, "use_thickness_curve", text="", icon='SPHERECURVE')
|
||||
sub = row.row(align=True)
|
||||
sub.active = settings.use_thickness_curve
|
||||
sub.popover(
|
||||
panel="TOPBAR_PT_gpencil_primitive",
|
||||
text="Thickness Profile",
|
||||
)
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.prop(settings, "use_thickness_curve", text="Use Thickness Profile")
|
||||
sub = row.row(align=True)
|
||||
if settings.use_thickness_curve:
|
||||
# Pressure curve.
|
||||
layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True)
|
||||
elif grease_pencil_tool == 'DRAW':
|
||||
layout.prop(gp_settings, "active_smooth_factor")
|
||||
row = layout.row(align=True)
|
||||
if compact:
|
||||
|
||||
@@ -1840,6 +1840,126 @@ class _defs_paint_grease_pencil:
|
||||
data_block='TINT',
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def grease_pencil_primitive_toolbar(context, layout, _tool, props):
|
||||
paint = context.tool_settings.gpencil_paint
|
||||
brush = paint.brush
|
||||
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
gp_settings = brush.gpencil_settings
|
||||
|
||||
row = layout.row(align=True)
|
||||
tool_settings = context.scene.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
brush_basic_grease_pencil_paint_settings,
|
||||
brush_basic__draw_color_selector,
|
||||
)
|
||||
|
||||
brush_basic__draw_color_selector(context, layout, brush, gp_settings, props)
|
||||
brush_basic_grease_pencil_paint_settings(layout, context, brush, compact=True)
|
||||
return True
|
||||
|
||||
@ToolDef.from_fn
|
||||
def line():
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("grease_pencil.primitive_line")
|
||||
_defs_paint_grease_pencil.grease_pencil_primitive_toolbar(context, layout, tool, props)
|
||||
|
||||
return dict(
|
||||
idname="builtin.line",
|
||||
label="Line",
|
||||
icon="ops.gpencil.primitive_line",
|
||||
cursor='CROSSHAIR',
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def polyline():
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("grease_pencil.primitive_polyline")
|
||||
_defs_paint_grease_pencil.grease_pencil_primitive_toolbar(context, layout, tool, props)
|
||||
|
||||
return dict(
|
||||
idname="builtin.polyline",
|
||||
label="Polyline",
|
||||
icon="ops.gpencil.primitive_polyline",
|
||||
cursor='CROSSHAIR',
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def arc():
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("grease_pencil.primitive_arc")
|
||||
_defs_paint_grease_pencil.grease_pencil_primitive_toolbar(context, layout, tool, props)
|
||||
|
||||
return dict(
|
||||
idname="builtin.arc",
|
||||
label="Arc",
|
||||
icon="ops.gpencil.primitive_arc",
|
||||
cursor='CROSSHAIR',
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def curve():
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("grease_pencil.primitive_curve")
|
||||
_defs_paint_grease_pencil.grease_pencil_primitive_toolbar(context, layout, tool, props)
|
||||
|
||||
return dict(
|
||||
idname="builtin.curve",
|
||||
label="Curve",
|
||||
icon="ops.gpencil.primitive_curve",
|
||||
cursor='CROSSHAIR',
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def box():
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("grease_pencil.primitive_box")
|
||||
_defs_paint_grease_pencil.grease_pencil_primitive_toolbar(context, layout, tool, props)
|
||||
|
||||
return dict(
|
||||
idname="builtin.box",
|
||||
label="Box",
|
||||
icon="ops.gpencil.primitive_box",
|
||||
cursor='CROSSHAIR',
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def circle():
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("grease_pencil.primitive_circle")
|
||||
_defs_paint_grease_pencil.grease_pencil_primitive_toolbar(context, layout, tool, props)
|
||||
|
||||
return dict(
|
||||
idname="builtin.circle",
|
||||
label="Circle",
|
||||
icon="ops.gpencil.primitive_circle",
|
||||
cursor='CROSSHAIR',
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
|
||||
class _defs_image_generic:
|
||||
|
||||
@@ -3248,6 +3368,13 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
||||
_defs_paint_grease_pencil.draw,
|
||||
_defs_paint_grease_pencil.erase,
|
||||
_defs_paint_grease_pencil.tint,
|
||||
None,
|
||||
_defs_paint_grease_pencil.line,
|
||||
_defs_paint_grease_pencil.polyline,
|
||||
_defs_paint_grease_pencil.arc,
|
||||
_defs_paint_grease_pencil.curve,
|
||||
_defs_paint_grease_pencil.box,
|
||||
_defs_paint_grease_pencil.circle,
|
||||
],
|
||||
'PAINT_GPENCIL': [
|
||||
_defs_view3d_generic.cursor,
|
||||
|
||||
@@ -13,6 +13,7 @@ set(INC
|
||||
../../windowmanager
|
||||
../../../../extern/curve_fit_nd
|
||||
../../geometry
|
||||
../sculpt_paint
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
@@ -28,6 +29,7 @@ set(SRC
|
||||
intern/grease_pencil_layers.cc
|
||||
intern/grease_pencil_material.cc
|
||||
intern/grease_pencil_ops.cc
|
||||
intern/grease_pencil_primitive.cc
|
||||
intern/grease_pencil_select.cc
|
||||
intern/grease_pencil_undo.cc
|
||||
intern/grease_pencil_utils.cc
|
||||
@@ -42,6 +44,7 @@ set(LIB
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::intern::clog
|
||||
extern_curve_fit_nd
|
||||
extern_fmtlib
|
||||
)
|
||||
|
||||
blender_add_lib(bf_editor_grease_pencil "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
@@ -129,6 +129,7 @@ void ED_operatortypes_grease_pencil()
|
||||
ED_operatortypes_grease_pencil_select();
|
||||
ED_operatortypes_grease_pencil_edit();
|
||||
ED_operatortypes_grease_pencil_material();
|
||||
ED_operatortypes_grease_pencil_primitives();
|
||||
}
|
||||
|
||||
void ED_operatormacros_grease_pencil()
|
||||
@@ -162,4 +163,5 @@ void ED_keymap_grease_pencil(wmKeyConfig *keyconf)
|
||||
keymap_grease_pencil_edit_mode(keyconf);
|
||||
keymap_grease_pencil_paint_mode(keyconf);
|
||||
keymap_grease_pencil_sculpt_mode(keyconf);
|
||||
ED_primitivetool_modal_keymap(keyconf);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,1481 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edgreasepencil
|
||||
* Operators for creating new Grease Pencil primitives (boxes, circles, ...).
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_grease_pencil.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_space_api.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
enum class PrimitiveType : int8_t {
|
||||
Line = 0,
|
||||
Polyline = 1,
|
||||
Arc = 2,
|
||||
Curve = 3,
|
||||
Box = 4,
|
||||
Circle = 5,
|
||||
};
|
||||
|
||||
enum class OperatorMode : int8_t {
|
||||
Idle = 0,
|
||||
Extruding = 1,
|
||||
/* Set the active control point to the mouse. */
|
||||
Grab = 2,
|
||||
/* Move the active control point. */
|
||||
Drag = 3,
|
||||
/* Move all control points. */
|
||||
DragAll = 4,
|
||||
/* Rotate all control points. */
|
||||
RotateAll = 5,
|
||||
/* Scale all control points. */
|
||||
ScaleAll = 6,
|
||||
};
|
||||
|
||||
enum class ControlPointType : int8_t {
|
||||
/* The points that are at the end of segments. */
|
||||
JoinPoint = 0,
|
||||
/* The points inside of the segments not including the end points. */
|
||||
HandlePoint = 1,
|
||||
};
|
||||
|
||||
enum class ModelKeyMode : int8_t {
|
||||
Cancel = 1,
|
||||
Confirm,
|
||||
Extrude,
|
||||
Panning,
|
||||
Grab,
|
||||
Rotate,
|
||||
Scale,
|
||||
IncreaseSubdivision,
|
||||
DecreaseSubdivision,
|
||||
};
|
||||
|
||||
static constexpr float ui_primary_point_draw_size_px = 8.0f;
|
||||
static constexpr float ui_secondary_point_draw_size_px = 5.0f;
|
||||
static constexpr float ui_tertiary_point_draw_size_px = 3.0f;
|
||||
static constexpr float ui_point_hit_size_px = 20.0f;
|
||||
static constexpr float ui_point_max_hit_size_px = 600.0f;
|
||||
|
||||
/* These three points are only used for `Box` and `Circle` type. */
|
||||
static constexpr int control_point_first = 0;
|
||||
static constexpr int control_point_center = 1;
|
||||
static constexpr int control_point_last = 2;
|
||||
|
||||
struct PrimitiveToolOperation {
|
||||
ARegion *region;
|
||||
/* For drawing preview loop. */
|
||||
void *draw_handle;
|
||||
ViewContext vc;
|
||||
|
||||
int segments;
|
||||
Vector<float3> control_points;
|
||||
/* Store the control points temporally. */
|
||||
Vector<float3> temp_control_points;
|
||||
int temp_segments;
|
||||
|
||||
PrimitiveType type;
|
||||
int subdivision;
|
||||
float4x4 projection;
|
||||
/* Helper class to project screen space coordinates to 3D. */
|
||||
DrawingPlacement placement;
|
||||
|
||||
bke::greasepencil::Drawing *drawing;
|
||||
BrushGpencilSettings *settings;
|
||||
float4 vertex_color;
|
||||
int material_index;
|
||||
float hardness;
|
||||
Brush *brush;
|
||||
|
||||
OperatorMode mode;
|
||||
float2 start_position_2d;
|
||||
int active_control_point_index;
|
||||
|
||||
ViewOpsData *vod;
|
||||
};
|
||||
|
||||
static int control_points_per_segment(const PrimitiveToolOperation &ptd)
|
||||
{
|
||||
switch (ptd.type) {
|
||||
case PrimitiveType::Polyline:
|
||||
case PrimitiveType::Line: {
|
||||
return 1;
|
||||
}
|
||||
case PrimitiveType::Box:
|
||||
case PrimitiveType::Circle:
|
||||
case PrimitiveType::Arc: {
|
||||
return 2;
|
||||
}
|
||||
case PrimitiveType::Curve: {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ControlPointType get_control_point_type(const PrimitiveToolOperation &ptd, const int point)
|
||||
{
|
||||
BLI_assert(point != -1);
|
||||
if (ELEM(ptd.type, PrimitiveType::Circle, PrimitiveType::Box)) {
|
||||
return ControlPointType::JoinPoint;
|
||||
}
|
||||
|
||||
const int num_shared_points = control_points_per_segment(ptd);
|
||||
if (math::mod(point, num_shared_points) == 0) {
|
||||
return ControlPointType::JoinPoint;
|
||||
}
|
||||
return ControlPointType::HandlePoint;
|
||||
}
|
||||
|
||||
static void control_point_colors_and_sizes(const PrimitiveToolOperation &ptd,
|
||||
MutableSpan<ColorGeometry4f> colors,
|
||||
MutableSpan<float> sizes)
|
||||
{
|
||||
ColorGeometry4f color_gizmo_primary;
|
||||
ColorGeometry4f color_gizmo_secondary;
|
||||
ColorGeometry4f color_gizmo_a;
|
||||
ColorGeometry4f color_gizmo_b;
|
||||
UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color_gizmo_primary);
|
||||
UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color_gizmo_secondary);
|
||||
UI_GetThemeColor4fv(TH_GIZMO_A, color_gizmo_a);
|
||||
UI_GetThemeColor4fv(TH_GIZMO_B, color_gizmo_b);
|
||||
|
||||
const float size_primary = ui_primary_point_draw_size_px;
|
||||
const float size_secondary = ui_secondary_point_draw_size_px;
|
||||
const float size_tertiary = ui_tertiary_point_draw_size_px;
|
||||
|
||||
if (ptd.segments == 0) {
|
||||
colors.fill(color_gizmo_primary);
|
||||
sizes.fill(size_primary);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ELEM(ptd.type, PrimitiveType::Box, PrimitiveType::Circle)) {
|
||||
colors.fill(color_gizmo_primary);
|
||||
sizes.fill(size_primary);
|
||||
|
||||
/* Set the center point's color. */
|
||||
colors[control_point_center] = color_gizmo_b;
|
||||
sizes[control_point_center] = size_secondary;
|
||||
}
|
||||
else {
|
||||
colors.fill(color_gizmo_secondary);
|
||||
sizes.fill(size_secondary);
|
||||
|
||||
for (const int i : colors.index_range()) {
|
||||
const ControlPointType control_point_type = get_control_point_type(ptd, i);
|
||||
|
||||
if (control_point_type == ControlPointType::JoinPoint) {
|
||||
colors[i] = color_gizmo_b;
|
||||
sizes[i] = size_tertiary;
|
||||
}
|
||||
}
|
||||
|
||||
colors.last() = color_gizmo_primary;
|
||||
sizes.last() = size_primary;
|
||||
|
||||
if (ELEM(ptd.type, PrimitiveType::Line, PrimitiveType::Polyline)) {
|
||||
colors.last(1) = color_gizmo_secondary;
|
||||
sizes.last(1) = size_primary;
|
||||
}
|
||||
}
|
||||
|
||||
const int active_index = ptd.active_control_point_index;
|
||||
if (active_index != -1) {
|
||||
sizes[active_index] *= 1.5;
|
||||
colors[active_index] = math::interpolate(colors[active_index], color_gizmo_a, 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_control_points(PrimitiveToolOperation &ptd)
|
||||
{
|
||||
GPUVertFormat *format3d = immVertexFormat();
|
||||
const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
|
||||
const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
const uint siz3d = GPU_vertformat_attr_add(format3d, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR);
|
||||
|
||||
GPU_program_point_size(true);
|
||||
immBegin(GPU_PRIM_POINTS, ptd.control_points.size());
|
||||
|
||||
Array<ColorGeometry4f> colors(ptd.control_points.size());
|
||||
Array<float> sizes(ptd.control_points.size());
|
||||
control_point_colors_and_sizes(ptd, colors, sizes);
|
||||
|
||||
for (const int point : ptd.control_points.index_range()) {
|
||||
const float3 point3d = ptd.control_points[point];
|
||||
const ColorGeometry4f color = colors[point];
|
||||
const float size = sizes[point];
|
||||
|
||||
immAttr4f(col3d, color[0], color[1], color[2], color[3]);
|
||||
immAttr1f(siz3d, size * 2.0f);
|
||||
immVertex3fv(pos3d, point3d);
|
||||
}
|
||||
|
||||
immEnd();
|
||||
immUnbindProgram();
|
||||
GPU_program_point_size(false);
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
|
||||
{
|
||||
PrimitiveToolOperation &ptd = *reinterpret_cast<PrimitiveToolOperation *>(arg);
|
||||
draw_control_points(ptd);
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_save(PrimitiveToolOperation &ptd)
|
||||
{
|
||||
ptd.temp_segments = ptd.segments;
|
||||
ptd.temp_control_points.resize(ptd.control_points.size());
|
||||
array_utils::copy(ptd.control_points.as_span(), ptd.temp_control_points.as_mutable_span());
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_load(PrimitiveToolOperation &ptd)
|
||||
{
|
||||
ptd.segments = ptd.temp_segments;
|
||||
ptd.control_points.resize(ptd.temp_control_points.size());
|
||||
array_utils::copy(ptd.temp_control_points.as_span(), ptd.control_points.as_mutable_span());
|
||||
}
|
||||
|
||||
static void primitive_calulate_curve_positions(PrimitiveToolOperation &ptd,
|
||||
Span<float2> control_points,
|
||||
MutableSpan<float2> new_positions)
|
||||
{
|
||||
const int subdivision = ptd.subdivision;
|
||||
const int new_points_num = new_positions.size();
|
||||
|
||||
if (ptd.segments == 0) {
|
||||
new_positions.fill(control_points.last());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ptd.type) {
|
||||
case PrimitiveType::Line:
|
||||
case PrimitiveType::Polyline: {
|
||||
for (const int i : new_positions.index_range().drop_back(1)) {
|
||||
const float t = math::mod(i / float(subdivision + 1), 1.0f);
|
||||
const int point = int(i / (subdivision + 1));
|
||||
const int point_next = point + 1;
|
||||
new_positions[i] = math::interpolate(control_points[point], control_points[point_next], t);
|
||||
}
|
||||
new_positions.last() = control_points.last();
|
||||
return;
|
||||
}
|
||||
case PrimitiveType::Arc: {
|
||||
const int num_shared_points = control_points_per_segment(ptd);
|
||||
const int num_segments = ptd.segments;
|
||||
for (const int segment_i : IndexRange(num_segments)) {
|
||||
const float2 A = control_points[num_shared_points * segment_i + 0];
|
||||
const float2 B = control_points[num_shared_points * segment_i + 1];
|
||||
const float2 C = control_points[num_shared_points * segment_i + 2];
|
||||
for (const int i : IndexRange(subdivision + 1)) {
|
||||
const float t = i / float(subdivision + 1);
|
||||
const float2 AB = math::interpolate(A, B, t);
|
||||
const float2 BC = math::interpolate(B, C, t);
|
||||
new_positions[i + segment_i * (subdivision + 1)] = math::interpolate(AB, BC, t);
|
||||
}
|
||||
}
|
||||
new_positions.last() = control_points.last();
|
||||
return;
|
||||
}
|
||||
case PrimitiveType::Curve: {
|
||||
const int num_shared_points = control_points_per_segment(ptd);
|
||||
const int num_segments = ptd.segments;
|
||||
|
||||
for (const int segment_i : IndexRange(num_segments)) {
|
||||
const float2 A = control_points[num_shared_points * segment_i + 0];
|
||||
const float2 B = control_points[num_shared_points * segment_i + 1];
|
||||
const float2 C = control_points[num_shared_points * segment_i + 2];
|
||||
const float2 D = control_points[num_shared_points * segment_i + 3];
|
||||
for (const int i : IndexRange(subdivision + 1)) {
|
||||
const float t = i / float(subdivision + 1);
|
||||
const float2 AB = math::interpolate(A, B, t);
|
||||
const float2 BC = math::interpolate(B, C, t);
|
||||
const float2 CD = math::interpolate(C, D, t);
|
||||
const float2 ABBC = math::interpolate(AB, BC, t);
|
||||
const float2 BCCD = math::interpolate(BC, CD, t);
|
||||
new_positions[i + segment_i * (subdivision + 1)] = math::interpolate(ABBC, BCCD, t);
|
||||
}
|
||||
}
|
||||
new_positions.last() = control_points.last();
|
||||
return;
|
||||
}
|
||||
case PrimitiveType::Circle: {
|
||||
const float2 center = control_points[control_point_center];
|
||||
const float2 offset = control_points[control_point_first] - center;
|
||||
for (const int i : new_positions.index_range()) {
|
||||
const float t = i / float(new_points_num);
|
||||
const float a = t * math::numbers::pi * 2.0f;
|
||||
new_positions[i] = offset * float2(sinf(a), cosf(a)) + center;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case PrimitiveType::Box: {
|
||||
const float2 center = control_points[control_point_center];
|
||||
const float2 offset = control_points[control_point_first] - center;
|
||||
/*
|
||||
* Calculate the 4 corners of the box.
|
||||
* Here's a diagram.
|
||||
*
|
||||
* +-----------+
|
||||
* |A B|
|
||||
* | |
|
||||
* | center |
|
||||
* | |
|
||||
* |D C|
|
||||
* +-----------+
|
||||
*
|
||||
*/
|
||||
const float2 A = center + offset * float2(1.0f, 1.0f);
|
||||
const float2 B = center + offset * float2(-1.0f, 1.0f);
|
||||
const float2 C = center + offset * float2(-1.0f, -1.0f);
|
||||
const float2 D = center + offset * float2(1.0f, -1.0f);
|
||||
const float2 corners[4] = {A, B, C, D};
|
||||
for (const int i : new_positions.index_range()) {
|
||||
const float t = math::mod(i / float(subdivision + 1), 1.0f);
|
||||
const int point = int(i / (subdivision + 1));
|
||||
const int point_next = math::mod(point + 1, 4);
|
||||
new_positions[i] = math::interpolate(corners[point], corners[point_next], t);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void primitive_calulate_curve_positions_2d(PrimitiveToolOperation &ptd,
|
||||
MutableSpan<float2> new_positions)
|
||||
{
|
||||
Array<float2> control_points_2d(ptd.control_points.size());
|
||||
for (const int i : ptd.control_points.index_range()) {
|
||||
control_points_2d[i] = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.control_points[i], ptd.projection);
|
||||
}
|
||||
|
||||
primitive_calulate_curve_positions(ptd, control_points_2d, new_positions);
|
||||
}
|
||||
|
||||
static int grease_pencil_primitive_curve_points_number(PrimitiveToolOperation &ptd)
|
||||
{
|
||||
const int subdivision = ptd.subdivision;
|
||||
|
||||
switch (ptd.type) {
|
||||
case PrimitiveType::Polyline:
|
||||
case PrimitiveType::Curve:
|
||||
case PrimitiveType::Line:
|
||||
case PrimitiveType::Circle:
|
||||
case PrimitiveType::Arc: {
|
||||
const int join_points = ptd.segments + 1;
|
||||
return join_points + subdivision * ptd.segments;
|
||||
break;
|
||||
}
|
||||
case PrimitiveType::Box: {
|
||||
return 4 + subdivision * 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_update_curves(PrimitiveToolOperation &ptd)
|
||||
{
|
||||
bke::CurvesGeometry &curves = ptd.drawing->strokes_for_write();
|
||||
|
||||
const int last_points_num = curves.points_by_curve()[curves.curves_range().last()].size();
|
||||
|
||||
const int new_points_num = grease_pencil_primitive_curve_points_number(ptd);
|
||||
|
||||
curves.resize(curves.points_num() - last_points_num + new_points_num, curves.curves_num());
|
||||
curves.offsets_for_write().last() = curves.points_num();
|
||||
const IndexRange curve_points = curves.points_by_curve()[curves.curves_range().last()];
|
||||
|
||||
MutableSpan<float3> positions_3d = curves.positions_for_write().slice(curve_points);
|
||||
Array<float2> positions_2d(new_points_num);
|
||||
|
||||
primitive_calulate_curve_positions_2d(ptd, positions_2d);
|
||||
ptd.placement.project(positions_2d, positions_3d);
|
||||
|
||||
MutableSpan<float> new_radii = ptd.drawing->radii_for_write().slice(curve_points);
|
||||
MutableSpan<float> new_opacities = ptd.drawing->opacities_for_write().slice(curve_points);
|
||||
MutableSpan<ColorGeometry4f> new_vertex_colors = ptd.drawing->vertex_colors_for_write().slice(
|
||||
curve_points);
|
||||
|
||||
new_vertex_colors.fill(ColorGeometry4f(ptd.vertex_color));
|
||||
|
||||
const ToolSettings *ts = ptd.vc.scene->toolsettings;
|
||||
const GP_Sculpt_Settings *gset = &ts->gp_sculpt;
|
||||
|
||||
for (const int point : curve_points.index_range()) {
|
||||
float pressure = 1.0f;
|
||||
/* Apply pressure curve. */
|
||||
if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
|
||||
const float t = point / float(new_points_num - 1);
|
||||
pressure = BKE_curvemapping_evaluateF(gset->cur_primitive, 0, t);
|
||||
}
|
||||
|
||||
const float radius = ed::greasepencil::radius_from_input_sample(
|
||||
pressure, positions_3d[point], ptd.vc, ptd.brush, ptd.vc.scene, ptd.settings);
|
||||
const float opacity = ed::greasepencil::opacity_from_input_sample(
|
||||
pressure, ptd.brush, ptd.vc.scene, ptd.settings);
|
||||
|
||||
new_radii[point] = radius;
|
||||
new_opacities[point] = opacity;
|
||||
}
|
||||
|
||||
ptd.drawing->tag_topology_changed();
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_init_curves(PrimitiveToolOperation &ptd)
|
||||
{
|
||||
/* Resize the curves geometry so there is one more curve with a single point. */
|
||||
bke::CurvesGeometry &curves = ptd.drawing->strokes_for_write();
|
||||
const int num_old_points = curves.points_num();
|
||||
curves.resize(curves.points_num() + 1, curves.curves_num() + 1);
|
||||
curves.offsets_for_write().last(1) = num_old_points;
|
||||
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
bke::SpanAttributeWriter<int> materials = attributes.lookup_or_add_for_write_span<int>(
|
||||
"material_index", bke::AttrDomain::Curve);
|
||||
bke::SpanAttributeWriter<bool> cyclic = attributes.lookup_or_add_for_write_span<bool>(
|
||||
"cyclic", bke::AttrDomain::Curve);
|
||||
bke::SpanAttributeWriter<float> hardnesses = attributes.lookup_or_add_for_write_span<float>(
|
||||
"hardness",
|
||||
bke::AttrDomain::Curve,
|
||||
bke::AttributeInitVArray(VArray<float>::ForSingle(1.0f, curves.curves_num())));
|
||||
|
||||
/* Only set the attribute if the type is not the default or if it already exists. */
|
||||
if (ptd.settings->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("start_cap")) {
|
||||
bke::SpanAttributeWriter<int8_t> start_caps = attributes.lookup_or_add_for_write_span<int8_t>(
|
||||
"start_cap", bke::AttrDomain::Curve);
|
||||
start_caps.span.last() = ptd.settings->caps_type;
|
||||
start_caps.finish();
|
||||
}
|
||||
|
||||
if (ptd.settings->caps_type != GP_STROKE_CAP_TYPE_ROUND || attributes.contains("end_cap")) {
|
||||
bke::SpanAttributeWriter<int8_t> end_caps = attributes.lookup_or_add_for_write_span<int8_t>(
|
||||
"end_cap", bke::AttrDomain::Curve);
|
||||
end_caps.span.last() = ptd.settings->caps_type;
|
||||
end_caps.finish();
|
||||
}
|
||||
|
||||
const bool is_cyclic = ELEM(ptd.type, PrimitiveType::Box, PrimitiveType::Circle);
|
||||
cyclic.span.last() = is_cyclic;
|
||||
materials.span.last() = ptd.material_index;
|
||||
hardnesses.span.last() = ptd.hardness;
|
||||
|
||||
cyclic.finish();
|
||||
materials.finish();
|
||||
hardnesses.finish();
|
||||
|
||||
curves.curve_types_for_write().last() = CURVE_TYPE_POLY;
|
||||
curves.update_curve_types();
|
||||
|
||||
/* Initialize the rest of the attributes with default values. */
|
||||
bke::fill_attribute_range_default(attributes,
|
||||
bke::AttrDomain::Point,
|
||||
{"position", "radius", "opacity", "vertex_color"},
|
||||
curves.points_range().take_back(1));
|
||||
bke::fill_attribute_range_default(
|
||||
attributes,
|
||||
bke::AttrDomain::Curve,
|
||||
{"curve_type", "material_index", "cyclic", "hardness", "start_cap", "end_cap"},
|
||||
curves.curves_range().take_back(1));
|
||||
|
||||
grease_pencil_primitive_update_curves(ptd);
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_undo_curves(PrimitiveToolOperation &ptd)
|
||||
{
|
||||
bke::CurvesGeometry &curves = ptd.drawing->strokes_for_write();
|
||||
curves.remove_curves(IndexMask({curves.curves_range().last(), 1}), {});
|
||||
ptd.drawing->tag_topology_changed();
|
||||
}
|
||||
|
||||
/* Helper: Draw status message while the user is running the operator. */
|
||||
static void grease_pencil_primitive_status_indicators(bContext *C,
|
||||
wmOperator *op,
|
||||
PrimitiveToolOperation &ptd)
|
||||
{
|
||||
std::string header;
|
||||
|
||||
switch (ptd.type) {
|
||||
case PrimitiveType::Line: {
|
||||
header += RPT_("Line: ");
|
||||
break;
|
||||
}
|
||||
case (PrimitiveType::Polyline): {
|
||||
header += RPT_("Polyline: ");
|
||||
break;
|
||||
}
|
||||
case (PrimitiveType::Box): {
|
||||
header += RPT_("Rectangle: ");
|
||||
break;
|
||||
}
|
||||
case (PrimitiveType::Circle): {
|
||||
header += RPT_("Circle: ");
|
||||
break;
|
||||
}
|
||||
case (PrimitiveType::Arc): {
|
||||
header += RPT_("Arc: ");
|
||||
break;
|
||||
}
|
||||
case (PrimitiveType::Curve): {
|
||||
header += RPT_("Curve: ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto get_modal_key_str = [&](ModelKeyMode id) {
|
||||
return WM_modalkeymap_operator_items_to_string(op->type, int(id), true).value_or("");
|
||||
};
|
||||
|
||||
header += fmt::format(IFACE_("{}: confirm, {}: cancel, Shift: align"),
|
||||
get_modal_key_str(ModelKeyMode::Confirm),
|
||||
get_modal_key_str(ModelKeyMode::Cancel));
|
||||
|
||||
header += fmt::format(IFACE_(", {}/{}: adjust subdivisions: {}"),
|
||||
get_modal_key_str(ModelKeyMode::IncreaseSubdivision),
|
||||
get_modal_key_str(ModelKeyMode::DecreaseSubdivision),
|
||||
int(ptd.subdivision));
|
||||
|
||||
if (ptd.segments == 1) {
|
||||
header += IFACE_(", Alt: center");
|
||||
}
|
||||
|
||||
if (ELEM(ptd.type,
|
||||
PrimitiveType::Line,
|
||||
PrimitiveType::Polyline,
|
||||
PrimitiveType::Arc,
|
||||
PrimitiveType::Curve))
|
||||
{
|
||||
header += fmt::format(IFACE_(", {}: extrude"), get_modal_key_str(ModelKeyMode::Extrude));
|
||||
}
|
||||
|
||||
header += fmt::format(IFACE_(", {}: grab, {}: rotate, {}: scale"),
|
||||
get_modal_key_str(ModelKeyMode::Grab),
|
||||
get_modal_key_str(ModelKeyMode::Rotate),
|
||||
get_modal_key_str(ModelKeyMode::Scale));
|
||||
|
||||
ED_workspace_status_text(C, header.c_str());
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_update_view(bContext *C, PrimitiveToolOperation &ptd)
|
||||
{
|
||||
GreasePencil *grease_pencil = static_cast<GreasePencil *>(ptd.vc.obact->data);
|
||||
|
||||
DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, grease_pencil);
|
||||
|
||||
ED_region_tag_redraw(ptd.region);
|
||||
}
|
||||
|
||||
/* Invoke handler: Initialize the operator. */
|
||||
static int grease_pencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
int return_value = ed::greasepencil::grease_pencil_draw_operator_invoke(C, op);
|
||||
if (return_value != OPERATOR_RUNNING_MODAL) {
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/* If in tools region, wait till we get to the main (3D-space)
|
||||
* region before allowing drawing to take place. */
|
||||
op->flag |= OP_IS_MODAL_CURSOR_REGION;
|
||||
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
/* Set cursor to indicate modal. */
|
||||
WM_cursor_modal_set(win, WM_CURSOR_CROSS);
|
||||
|
||||
ViewContext vc = ED_view3d_viewcontext_init(C, CTX_data_depsgraph_pointer(C));
|
||||
|
||||
/* Allocate new data. */
|
||||
PrimitiveToolOperation *ptd_pointer = MEM_new<PrimitiveToolOperation>(__func__);
|
||||
op->customdata = ptd_pointer;
|
||||
|
||||
PrimitiveToolOperation &ptd = *ptd_pointer;
|
||||
|
||||
ptd.vc = vc;
|
||||
ptd.region = vc.region;
|
||||
View3D *view3d = CTX_wm_view3d(C);
|
||||
const float2 start_coords = float2(event->mval);
|
||||
|
||||
GreasePencil *grease_pencil = static_cast<GreasePencil *>(vc.obact->data);
|
||||
|
||||
/* Initialize helper class for projecting screen space coordinates. */
|
||||
DrawingPlacement placement = DrawingPlacement(
|
||||
*vc.scene, *vc.region, *view3d, *vc.obact, *grease_pencil->get_active_layer());
|
||||
if (placement.use_project_to_surface()) {
|
||||
placement.cache_viewport_depths(CTX_data_depsgraph_pointer(C), vc.region, view3d);
|
||||
}
|
||||
else if (placement.use_project_to_nearest_stroke()) {
|
||||
placement.cache_viewport_depths(CTX_data_depsgraph_pointer(C), vc.region, view3d);
|
||||
placement.set_origin_to_nearest_stroke(start_coords);
|
||||
}
|
||||
|
||||
ptd.placement = placement;
|
||||
|
||||
ptd.vod = ED_view3d_navigation_init(C, nullptr);
|
||||
|
||||
ptd.start_position_2d = start_coords;
|
||||
ptd.subdivision = RNA_int_get(op->ptr, "subdivision");
|
||||
ptd.type = PrimitiveType(RNA_enum_get(op->ptr, "type"));
|
||||
const float3 pos = ptd.placement.project(ptd.start_position_2d);
|
||||
ptd.segments = 0;
|
||||
ptd.control_points = Vector<float3>({pos});
|
||||
|
||||
grease_pencil_primitive_save(ptd);
|
||||
|
||||
ptd.mode = OperatorMode::Extruding;
|
||||
ptd.segments++;
|
||||
ptd.control_points.append_n_times(pos, control_points_per_segment(ptd));
|
||||
ptd.active_control_point_index = -1;
|
||||
ptd.projection = ED_view3d_ob_project_mat_get(ptd.vc.rv3d, ptd.vc.obact);
|
||||
|
||||
Paint *paint = &vc.scene->toolsettings->gp_paint->paint;
|
||||
ptd.brush = BKE_paint_brush(paint);
|
||||
ptd.settings = ptd.brush->gpencil_settings;
|
||||
|
||||
BKE_curvemapping_init(ptd.settings->curve_sensitivity);
|
||||
BKE_curvemapping_init(ptd.settings->curve_strength);
|
||||
BKE_curvemapping_init(ptd.settings->curve_jitter);
|
||||
BKE_curvemapping_init(ptd.settings->curve_rand_pressure);
|
||||
BKE_curvemapping_init(ptd.settings->curve_rand_strength);
|
||||
BKE_curvemapping_init(ptd.settings->curve_rand_uv);
|
||||
BKE_curvemapping_init(ptd.settings->curve_rand_hue);
|
||||
BKE_curvemapping_init(ptd.settings->curve_rand_saturation);
|
||||
BKE_curvemapping_init(ptd.settings->curve_rand_value);
|
||||
|
||||
ToolSettings *ts = vc.scene->toolsettings;
|
||||
GP_Sculpt_Settings *gset = &ts->gp_sculpt;
|
||||
/* Initialize pressure curve. */
|
||||
if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
|
||||
BKE_curvemapping_init(ts->gp_sculpt.cur_primitive);
|
||||
}
|
||||
|
||||
Material *material = BKE_grease_pencil_object_material_ensure_from_active_input_brush(
|
||||
CTX_data_main(C), vc.obact, ptd.brush);
|
||||
ptd.material_index = BKE_object_material_index_get(vc.obact, material);
|
||||
|
||||
const bool use_vertex_color = (vc.scene->toolsettings->gp_paint->mode ==
|
||||
GPPAINT_FLAG_USE_VERTEXCOLOR);
|
||||
const bool use_vertex_color_stroke = use_vertex_color && ELEM(ptd.settings->vertex_mode,
|
||||
GPPAINT_MODE_STROKE,
|
||||
GPPAINT_MODE_BOTH);
|
||||
ptd.vertex_color = use_vertex_color_stroke ? float4(ptd.brush->rgb[0],
|
||||
ptd.brush->rgb[1],
|
||||
ptd.brush->rgb[2],
|
||||
ptd.settings->vertex_factor) :
|
||||
float4(0.0f);
|
||||
srgb_to_linearrgb_v4(ptd.vertex_color, ptd.vertex_color);
|
||||
|
||||
/* TODO: Add UI for hardness. */
|
||||
ptd.hardness = 1.0f;
|
||||
|
||||
BLI_assert(grease_pencil->has_active_layer());
|
||||
ptd.drawing = grease_pencil->get_editable_drawing_at(*grease_pencil->get_active_layer(),
|
||||
vc.scene->r.cfra);
|
||||
|
||||
grease_pencil_primitive_init_curves(ptd);
|
||||
grease_pencil_primitive_update_view(C, ptd);
|
||||
|
||||
ptd.draw_handle = ED_region_draw_cb_activate(
|
||||
ptd.region->type, grease_pencil_primitive_draw, ptd_pointer, REGION_DRAW_POST_VIEW);
|
||||
|
||||
/* Updates indicator in header. */
|
||||
grease_pencil_primitive_status_indicators(C, op, ptd);
|
||||
|
||||
/* Add a modal handler for this operator. */
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Exit and free memory. */
|
||||
static void grease_pencil_primitive_exit(bContext *C, wmOperator *op)
|
||||
{
|
||||
PrimitiveToolOperation *ptd = static_cast<PrimitiveToolOperation *>(op->customdata);
|
||||
|
||||
/* Clear status message area. */
|
||||
ED_workspace_status_text(C, nullptr);
|
||||
|
||||
WM_cursor_modal_restore(ptd->vc.win);
|
||||
|
||||
/* Deactivate the extra drawing stuff in 3D-View. */
|
||||
ED_region_draw_cb_exit(ptd->region->type, ptd->draw_handle);
|
||||
|
||||
ED_view3d_navigation_free(C, ptd->vod);
|
||||
|
||||
grease_pencil_primitive_update_view(C, *ptd);
|
||||
|
||||
MEM_delete<PrimitiveToolOperation>(ptd);
|
||||
/* Clear pointer. */
|
||||
op->customdata = nullptr;
|
||||
}
|
||||
|
||||
static float2 snap_diagonals(float2 p)
|
||||
{
|
||||
using namespace math;
|
||||
return sign(p) * float2(1.0f / numbers::sqrt2) * length(p);
|
||||
}
|
||||
|
||||
/* Using Chebyshev distance instead of Euclidean. */
|
||||
static float2 snap_diagonals_box(float2 p)
|
||||
{
|
||||
using namespace math;
|
||||
return sign(p) * float2(std::max(abs(p[0]), abs(p[1])));
|
||||
}
|
||||
|
||||
/* Snaps to the closest diagonal, horizontal or vertical. */
|
||||
static float2 snap_8_angles(float2 p)
|
||||
{
|
||||
using namespace math;
|
||||
/* sin(pi/8) or sin of 22.5 degrees.*/
|
||||
const float sin225 = 0.3826834323650897717284599840304f;
|
||||
return sign(p) * length(p) * normalize(sign(normalize(abs(p)) - sin225) + 1.0f);
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_extruding_update(PrimitiveToolOperation &ptd,
|
||||
const wmEvent *event)
|
||||
{
|
||||
using namespace math;
|
||||
const float2 start = ptd.start_position_2d;
|
||||
const float2 end = float2(event->mval);
|
||||
|
||||
const float2 dif = end - start;
|
||||
float2 offset = dif;
|
||||
|
||||
if (event->modifier & KM_SHIFT) {
|
||||
if (ptd.type == PrimitiveType::Box) {
|
||||
offset = snap_diagonals_box(dif);
|
||||
}
|
||||
else if (ptd.type == PrimitiveType::Circle) {
|
||||
offset = snap_diagonals(dif);
|
||||
}
|
||||
else { /* Line, Polyline, Arc and Curve. */
|
||||
offset = snap_8_angles(dif);
|
||||
}
|
||||
}
|
||||
offset *= 0.5f;
|
||||
|
||||
float2 center = start + offset;
|
||||
|
||||
if (event->modifier & KM_ALT && ptd.segments == 1) {
|
||||
center = start;
|
||||
offset *= 2.0f;
|
||||
}
|
||||
|
||||
const float3 start_pos = ptd.placement.project(center - offset);
|
||||
const float3 end_pos = ptd.placement.project(center + offset);
|
||||
|
||||
const int number_control_points = control_points_per_segment(ptd);
|
||||
for (const int i : IndexRange(number_control_points + 1)) {
|
||||
ptd.control_points.last(i) = interpolate(
|
||||
end_pos, start_pos, (i / float(number_control_points)));
|
||||
}
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_drag_all_update(PrimitiveToolOperation &ptd,
|
||||
const wmEvent *event)
|
||||
{
|
||||
const float2 start = ptd.start_position_2d;
|
||||
const float2 end = float2(event->mval);
|
||||
const float2 dif = end - start;
|
||||
|
||||
for (const int point_index : ptd.control_points.index_range()) {
|
||||
const float2 start_pos2 = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.temp_control_points[point_index], ptd.projection);
|
||||
|
||||
float3 pos = ptd.placement.project(start_pos2 + dif);
|
||||
ptd.control_points[point_index] = pos;
|
||||
}
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_grab_update(PrimitiveToolOperation &ptd, const wmEvent *event)
|
||||
{
|
||||
BLI_assert(ptd.active_control_point_index != -1);
|
||||
const float3 pos = ptd.placement.project(float2(event->mval));
|
||||
ptd.control_points[ptd.active_control_point_index] = pos;
|
||||
|
||||
if (!ELEM(ptd.type, PrimitiveType::Circle, PrimitiveType::Box)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the center point is been grabbed, move all points. */
|
||||
if (ptd.active_control_point_index == control_point_center) {
|
||||
grease_pencil_primitive_drag_all_update(ptd, event);
|
||||
return;
|
||||
}
|
||||
|
||||
const int other_point = ptd.active_control_point_index == control_point_first ?
|
||||
control_point_last :
|
||||
control_point_first;
|
||||
|
||||
/* Get the location of the other control point.*/
|
||||
const float2 other_point_2d = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.temp_control_points[other_point], ptd.projection);
|
||||
|
||||
/* Set the center point to between the first and last point. */
|
||||
ptd.control_points[control_point_center] = ptd.placement.project(
|
||||
(other_point_2d + float2(event->mval)) / 2.0f);
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_drag_update(PrimitiveToolOperation &ptd, const wmEvent *event)
|
||||
{
|
||||
BLI_assert(ptd.active_control_point_index != -1);
|
||||
const float2 start = ptd.start_position_2d;
|
||||
const float2 end = float2(event->mval);
|
||||
const float2 dif = end - start;
|
||||
|
||||
const float2 start_pos2 = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.temp_control_points[ptd.active_control_point_index], ptd.projection);
|
||||
|
||||
const float3 pos = ptd.placement.project(start_pos2 + dif);
|
||||
ptd.control_points[ptd.active_control_point_index] = pos;
|
||||
}
|
||||
|
||||
static float2 primitive_center_of_mass(const PrimitiveToolOperation &ptd)
|
||||
{
|
||||
if (ELEM(ptd.type, PrimitiveType::Box, PrimitiveType::Circle)) {
|
||||
return ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.temp_control_points[control_point_center], ptd.projection);
|
||||
}
|
||||
float2 center_of_mass = float2(0.0f, 0.0f);
|
||||
|
||||
for (const int point_index : ptd.control_points.index_range()) {
|
||||
center_of_mass += ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.temp_control_points[point_index], ptd.projection);
|
||||
}
|
||||
center_of_mass /= ptd.control_points.size();
|
||||
return center_of_mass;
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_rotate_all_update(PrimitiveToolOperation &ptd,
|
||||
const wmEvent *event)
|
||||
{
|
||||
const float2 start = ptd.start_position_2d;
|
||||
const float2 end = float2(event->mval);
|
||||
|
||||
const float2 center_of_mass = primitive_center_of_mass(ptd);
|
||||
|
||||
const float2 end_ = end - center_of_mass;
|
||||
const float2 start_ = start - center_of_mass;
|
||||
const float rotation = math::atan2(start_[0], start_[1]) - math::atan2(end_[0], end_[1]);
|
||||
|
||||
for (const int point_index : ptd.control_points.index_range()) {
|
||||
const float2 start_pos2 = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.temp_control_points[point_index], ptd.projection);
|
||||
|
||||
const float2 dif = start_pos2 - center_of_mass;
|
||||
const float c = math::cos(rotation);
|
||||
const float s = math::sin(rotation);
|
||||
const float2x2 rot = float2x2(float2(c, s), float2(-s, c));
|
||||
const float2 pos2 = rot * dif + center_of_mass;
|
||||
const float3 pos = ptd.placement.project(pos2);
|
||||
ptd.control_points[point_index] = pos;
|
||||
}
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_scale_all_update(PrimitiveToolOperation &ptd,
|
||||
const wmEvent *event)
|
||||
{
|
||||
const float2 start = ptd.start_position_2d;
|
||||
const float2 end = float2(event->mval);
|
||||
|
||||
const float2 center_of_mass = primitive_center_of_mass(ptd);
|
||||
|
||||
const float scale = math::length(end - center_of_mass) / math::length(start - center_of_mass);
|
||||
|
||||
for (const int point_index : ptd.control_points.index_range()) {
|
||||
const float2 start_pos2 = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.temp_control_points[point_index], ptd.projection);
|
||||
|
||||
const float2 pos2 = (start_pos2 - center_of_mass) * scale + center_of_mass;
|
||||
const float3 pos = ptd.placement.project(pos2);
|
||||
ptd.control_points[point_index] = pos;
|
||||
}
|
||||
}
|
||||
|
||||
static int primitive_check_ui_hover(const PrimitiveToolOperation &ptd, const wmEvent *event)
|
||||
{
|
||||
float closest_distance_squared = std::numeric_limits<float>::max();
|
||||
int closest_point = -1;
|
||||
|
||||
for (const int i : ptd.control_points.index_range()) {
|
||||
const int point = (ptd.control_points.size() - 1) - i;
|
||||
const float2 pos_proj = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.control_points[point], ptd.projection);
|
||||
const float radius_sq = ui_point_hit_size_px * ui_point_hit_size_px;
|
||||
const float distance_squared = math::distance_squared(pos_proj, float2(event->mval));
|
||||
/* If the mouse is over a control point. */
|
||||
if (distance_squared <= radius_sq) {
|
||||
return point;
|
||||
}
|
||||
|
||||
const ControlPointType control_point_type = get_control_point_type(ptd, point);
|
||||
|
||||
/* Save the closest handle point. */
|
||||
if (distance_squared < closest_distance_squared &&
|
||||
control_point_type == ControlPointType::HandlePoint &&
|
||||
distance_squared < ui_point_max_hit_size_px * ui_point_max_hit_size_px)
|
||||
{
|
||||
closest_point = point;
|
||||
closest_distance_squared = distance_squared;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_point != -1) {
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_cursor_update(bContext *C,
|
||||
PrimitiveToolOperation &ptd,
|
||||
const wmEvent *event)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
if (ptd.mode != OperatorMode::Idle) {
|
||||
WM_cursor_modal_set(win, WM_CURSOR_CROSS);
|
||||
return;
|
||||
}
|
||||
|
||||
const int ui_id = primitive_check_ui_hover(ptd, event);
|
||||
ptd.active_control_point_index = ui_id;
|
||||
if (ui_id == -1) {
|
||||
if (ptd.type == PrimitiveType::Polyline) {
|
||||
WM_cursor_modal_set(win, WM_CURSOR_CROSS);
|
||||
return;
|
||||
}
|
||||
|
||||
WM_cursor_modal_set(win, WM_CURSOR_HAND);
|
||||
return;
|
||||
}
|
||||
|
||||
WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL);
|
||||
return;
|
||||
}
|
||||
|
||||
static int grease_pencil_primitive_event_model_map(bContext *C,
|
||||
wmOperator *op,
|
||||
PrimitiveToolOperation &ptd,
|
||||
const wmEvent *event)
|
||||
{
|
||||
switch (event->val) {
|
||||
case int(ModelKeyMode::Cancel): {
|
||||
grease_pencil_primitive_undo_curves(ptd);
|
||||
grease_pencil_primitive_exit(C, op);
|
||||
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
case int(ModelKeyMode::Confirm): {
|
||||
grease_pencil_primitive_exit(C, op);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
case int(ModelKeyMode::Extrude): {
|
||||
if (ptd.mode == OperatorMode::Idle &&
|
||||
ELEM(ptd.type, PrimitiveType::Line, PrimitiveType::Arc, PrimitiveType::Curve))
|
||||
{
|
||||
ptd.mode = OperatorMode::Extruding;
|
||||
grease_pencil_primitive_save(ptd);
|
||||
|
||||
ptd.start_position_2d = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.control_points.last(), ptd.projection);
|
||||
const float3 pos = ptd.placement.project(ptd.start_position_2d);
|
||||
|
||||
const int number_control_points = control_points_per_segment(ptd);
|
||||
ptd.control_points.append_n_times(pos, number_control_points);
|
||||
ptd.active_control_point_index = -1;
|
||||
ptd.segments++;
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
if (ptd.type == PrimitiveType::Polyline &&
|
||||
ELEM(ptd.mode, OperatorMode::Idle, OperatorMode::Extruding))
|
||||
{
|
||||
ptd.mode = OperatorMode::Extruding;
|
||||
grease_pencil_primitive_save(ptd);
|
||||
|
||||
ptd.start_position_2d = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.control_points.last(), ptd.projection);
|
||||
ptd.active_control_point_index = -1;
|
||||
const float3 pos = ptd.placement.project(float2(event->mval));
|
||||
|
||||
/* If we have only two points and they're the same then don't extrude new a point. */
|
||||
if (ptd.segments == 1 &&
|
||||
math::distance_squared(ptd.control_points.first(), ptd.control_points.last()) == 0.0f)
|
||||
{
|
||||
ptd.control_points.last() = pos;
|
||||
}
|
||||
else {
|
||||
ptd.control_points.append(pos);
|
||||
ptd.segments++;
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
case int(ModelKeyMode::Grab): {
|
||||
if (ptd.mode == OperatorMode::Idle) {
|
||||
ptd.start_position_2d = float2(event->mval);
|
||||
ptd.mode = OperatorMode::DragAll;
|
||||
|
||||
grease_pencil_primitive_save(ptd);
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
case int(ModelKeyMode::Rotate): {
|
||||
if (ptd.mode == OperatorMode::Idle) {
|
||||
ptd.start_position_2d = float2(event->mval);
|
||||
ptd.mode = OperatorMode::RotateAll;
|
||||
|
||||
grease_pencil_primitive_save(ptd);
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
case int(ModelKeyMode::Scale): {
|
||||
if (ptd.mode == OperatorMode::Idle) {
|
||||
ptd.start_position_2d = float2(event->mval);
|
||||
ptd.mode = OperatorMode::ScaleAll;
|
||||
|
||||
grease_pencil_primitive_save(ptd);
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
case int(ModelKeyMode::IncreaseSubdivision): {
|
||||
if (event->val != KM_RELEASE) {
|
||||
ptd.subdivision++;
|
||||
RNA_int_set(op->ptr, "subdivision", ptd.subdivision);
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
case int(ModelKeyMode::DecreaseSubdivision): {
|
||||
if (event->val != KM_RELEASE) {
|
||||
ptd.subdivision--;
|
||||
ptd.subdivision = std::max(ptd.subdivision, 0);
|
||||
RNA_int_set(op->ptr, "subdivision", ptd.subdivision);
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int grease_pencil_primitive_mouse_event(PrimitiveToolOperation &ptd, const wmEvent *event)
|
||||
{
|
||||
if (event->val == KM_RELEASE && ELEM(ptd.mode,
|
||||
OperatorMode::Grab,
|
||||
OperatorMode::Drag,
|
||||
OperatorMode::Extruding,
|
||||
OperatorMode::DragAll,
|
||||
OperatorMode::RotateAll,
|
||||
OperatorMode::ScaleAll))
|
||||
{
|
||||
ptd.mode = OperatorMode::Idle;
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
if (ptd.mode == OperatorMode::Idle && event->val == KM_PRESS) {
|
||||
const int ui_id = primitive_check_ui_hover(ptd, event);
|
||||
ptd.active_control_point_index = ui_id;
|
||||
if (ui_id == -1) {
|
||||
if (ptd.type != PrimitiveType::Polyline) {
|
||||
ptd.start_position_2d = float2(event->mval);
|
||||
ptd.mode = OperatorMode::DragAll;
|
||||
|
||||
grease_pencil_primitive_save(ptd);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ControlPointType control_point_type = get_control_point_type(ptd, ui_id);
|
||||
|
||||
if (control_point_type == ControlPointType::JoinPoint) {
|
||||
ptd.start_position_2d = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.control_points[ptd.active_control_point_index], ptd.projection);
|
||||
ptd.mode = OperatorMode::Grab;
|
||||
|
||||
grease_pencil_primitive_save(ptd);
|
||||
}
|
||||
else if (control_point_type == ControlPointType::HandlePoint) {
|
||||
ptd.start_position_2d = float2(event->mval);
|
||||
ptd.mode = OperatorMode::Drag;
|
||||
|
||||
grease_pencil_primitive_save(ptd);
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptd.type == PrimitiveType::Polyline && ptd.mode == OperatorMode::Idle &&
|
||||
event->val == KM_PRESS)
|
||||
{
|
||||
ptd.mode = OperatorMode::Extruding;
|
||||
grease_pencil_primitive_save(ptd);
|
||||
|
||||
ptd.start_position_2d = ED_view3d_project_float_v2_m4(
|
||||
ptd.vc.region, ptd.control_points.last(), ptd.projection);
|
||||
const float3 pos = ptd.placement.project(float2(event->mval));
|
||||
|
||||
/* If we have only two points and they're the same then don't extrude new a point. */
|
||||
if (ptd.segments == 1 &&
|
||||
math::distance_squared(ptd.control_points.first(), ptd.control_points.last()) == 0.0f)
|
||||
{
|
||||
ptd.control_points.last() = pos;
|
||||
}
|
||||
else {
|
||||
ptd.control_points.append(pos);
|
||||
ptd.segments++;
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_operator_update(PrimitiveToolOperation &ptd,
|
||||
const wmEvent *event)
|
||||
{
|
||||
switch (ptd.mode) {
|
||||
case OperatorMode::Extruding: {
|
||||
grease_pencil_primitive_extruding_update(ptd, event);
|
||||
break;
|
||||
}
|
||||
case OperatorMode::Grab: {
|
||||
grease_pencil_primitive_grab_update(ptd, event);
|
||||
break;
|
||||
}
|
||||
case OperatorMode::Drag: {
|
||||
grease_pencil_primitive_drag_update(ptd, event);
|
||||
break;
|
||||
}
|
||||
case OperatorMode::DragAll: {
|
||||
grease_pencil_primitive_drag_all_update(ptd, event);
|
||||
break;
|
||||
}
|
||||
case OperatorMode::ScaleAll: {
|
||||
grease_pencil_primitive_scale_all_update(ptd, event);
|
||||
break;
|
||||
}
|
||||
case OperatorMode::RotateAll: {
|
||||
grease_pencil_primitive_rotate_all_update(ptd, event);
|
||||
break;
|
||||
}
|
||||
case OperatorMode::Idle: {
|
||||
/* Do nothing. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Modal handler: Events handling during interactive part. */
|
||||
static int grease_pencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
PrimitiveToolOperation &ptd = *reinterpret_cast<PrimitiveToolOperation *>(op->customdata);
|
||||
|
||||
ptd.projection = ED_view3d_ob_project_mat_get(ptd.vc.rv3d, ptd.vc.obact);
|
||||
grease_pencil_primitive_cursor_update(C, ptd, event);
|
||||
|
||||
if (event->type == EVT_MODAL_MAP) {
|
||||
const int return_val = grease_pencil_primitive_event_model_map(C, op, ptd, event);
|
||||
if (return_val != OPERATOR_RUNNING_MODAL) {
|
||||
return return_val;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case LEFTMOUSE: {
|
||||
const int return_val = grease_pencil_primitive_mouse_event(ptd, event);
|
||||
if (return_val != OPERATOR_RUNNING_MODAL) {
|
||||
return return_val;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case RIGHTMOUSE: {
|
||||
if (event->val != KM_PRESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ptd.mode == OperatorMode::Idle) {
|
||||
grease_pencil_primitive_undo_curves(ptd);
|
||||
grease_pencil_primitive_exit(C, op);
|
||||
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
else {
|
||||
ptd.mode = OperatorMode::Idle;
|
||||
|
||||
grease_pencil_primitive_load(ptd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Updating is done every event not just `MOUSEMOVE`. */
|
||||
grease_pencil_primitive_operator_update(ptd, event);
|
||||
grease_pencil_primitive_update_curves(ptd);
|
||||
|
||||
/* Updates indicator in header. */
|
||||
grease_pencil_primitive_status_indicators(C, op, ptd);
|
||||
grease_pencil_primitive_update_view(C, ptd);
|
||||
|
||||
/* Still running... */
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* Cancel handler. */
|
||||
static void grease_pencil_primitive_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
/* This is just a wrapper around exit() */
|
||||
grease_pencil_primitive_exit(C, op);
|
||||
}
|
||||
|
||||
static void grease_pencil_primitive_common_props(wmOperatorType *ot,
|
||||
const int default_subdiv,
|
||||
const PrimitiveType default_type)
|
||||
{
|
||||
static const EnumPropertyItem grease_pencil_primitive_type[] = {
|
||||
{int(PrimitiveType::Box), "BOX", 0, "Box", ""},
|
||||
{int(PrimitiveType::Line), "LINE", 0, "Line", ""},
|
||||
{int(PrimitiveType::Polyline), "POLYLINE", 0, "Polyline", ""},
|
||||
{int(PrimitiveType::Circle), "CIRCLE", 0, "Circle", ""},
|
||||
{int(PrimitiveType::Arc), "ARC", 0, "Arc", ""},
|
||||
{int(PrimitiveType::Curve), "CURVE", 0, "Curve", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_int(ot->srna,
|
||||
"subdivision",
|
||||
default_subdiv,
|
||||
0,
|
||||
INT_MAX,
|
||||
"Subdivisions",
|
||||
"Number of subdivisions per segment",
|
||||
0,
|
||||
INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
RNA_def_enum(
|
||||
ot->srna, "type", grease_pencil_primitive_type, int(default_type), "Type", "Type of shape");
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_primitive_line(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Grease Pencil Line Shape";
|
||||
ot->idname = "GREASE_PENCIL_OT_primitive_line";
|
||||
ot->description = "Create predefined grease pencil stroke lines";
|
||||
|
||||
/* Callbacks. */
|
||||
ot->invoke = grease_pencil_primitive_invoke;
|
||||
ot->modal = grease_pencil_primitive_modal;
|
||||
ot->cancel = grease_pencil_primitive_cancel;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties and Flags. */
|
||||
grease_pencil_primitive_common_props(ot, 6, PrimitiveType::Line);
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_primitive_polyline(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Grease Pencil Polyline Shape";
|
||||
ot->idname = "GREASE_PENCIL_OT_primitive_polyline";
|
||||
ot->description = "Create predefined grease pencil stroke polylines";
|
||||
|
||||
/* Callbacks. */
|
||||
ot->invoke = grease_pencil_primitive_invoke;
|
||||
ot->modal = grease_pencil_primitive_modal;
|
||||
ot->cancel = grease_pencil_primitive_cancel;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties. */
|
||||
grease_pencil_primitive_common_props(ot, 6, PrimitiveType::Polyline);
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_primitive_arc(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Grease Pencil Arc Shape";
|
||||
ot->idname = "GREASE_PENCIL_OT_primitive_arc";
|
||||
ot->description = "Create predefined grease pencil stroke arcs";
|
||||
|
||||
/* Callbacks. */
|
||||
ot->invoke = grease_pencil_primitive_invoke;
|
||||
ot->modal = grease_pencil_primitive_modal;
|
||||
ot->cancel = grease_pencil_primitive_cancel;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties. */
|
||||
grease_pencil_primitive_common_props(ot, 62, PrimitiveType::Arc);
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_primitive_curve(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Grease Pencil Curve Shape";
|
||||
ot->idname = "GREASE_PENCIL_OT_primitive_curve";
|
||||
ot->description = "Create predefined grease pencil stroke curve shapes";
|
||||
|
||||
/* Callbacks. */
|
||||
ot->invoke = grease_pencil_primitive_invoke;
|
||||
ot->modal = grease_pencil_primitive_modal;
|
||||
ot->cancel = grease_pencil_primitive_cancel;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties. */
|
||||
grease_pencil_primitive_common_props(ot, 62, PrimitiveType::Curve);
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_primitive_box(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Grease Pencil Box Shape";
|
||||
ot->idname = "GREASE_PENCIL_OT_primitive_box";
|
||||
ot->description = "Create predefined grease pencil stroke boxes";
|
||||
|
||||
/* Callbacks. */
|
||||
ot->invoke = grease_pencil_primitive_invoke;
|
||||
ot->modal = grease_pencil_primitive_modal;
|
||||
ot->cancel = grease_pencil_primitive_cancel;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties. */
|
||||
grease_pencil_primitive_common_props(ot, 3, PrimitiveType::Box);
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_primitive_circle(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
ot->name = "Grease Pencil Circle Shape";
|
||||
ot->idname = "GREASE_PENCIL_OT_primitive_circle";
|
||||
ot->description = "Create predefined grease pencil stroke circles";
|
||||
|
||||
/* Callbacks. */
|
||||
ot->invoke = grease_pencil_primitive_invoke;
|
||||
ot->modal = grease_pencil_primitive_modal;
|
||||
ot->cancel = grease_pencil_primitive_cancel;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
||||
|
||||
/* Properties. */
|
||||
grease_pencil_primitive_common_props(ot, 94, PrimitiveType::Circle);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
void ED_operatortypes_grease_pencil_primitives()
|
||||
{
|
||||
using namespace blender::ed::greasepencil;
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_primitive_line);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_primitive_polyline);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_primitive_arc);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_primitive_curve);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_primitive_box);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_primitive_circle);
|
||||
}
|
||||
|
||||
void ED_primitivetool_modal_keymap(wmKeyConfig *keyconf)
|
||||
{
|
||||
using namespace blender::ed::greasepencil;
|
||||
static const EnumPropertyItem modal_items[] = {
|
||||
{int(ModelKeyMode::Cancel), "CANCEL", 0, "Cancel", ""},
|
||||
{int(ModelKeyMode::Confirm), "CONFIRM", 0, "Confirm", ""},
|
||||
{int(ModelKeyMode::Panning), "PANNING", 0, "Panning", ""},
|
||||
{int(ModelKeyMode::Extrude), "EXTRUDE", 0, "Extrude", ""},
|
||||
{int(ModelKeyMode::Grab), "GRAB", 0, "Grab", ""},
|
||||
{int(ModelKeyMode::Rotate), "ROTATE", 0, "Rotate", ""},
|
||||
{int(ModelKeyMode::Scale), "SCALE", 0, "Scale", ""},
|
||||
{int(ModelKeyMode::IncreaseSubdivision),
|
||||
"INCREASE_SUBDIVISION",
|
||||
0,
|
||||
"increase_subdivision",
|
||||
""},
|
||||
{int(ModelKeyMode::DecreaseSubdivision),
|
||||
"DECREASE_SUBDIVISION",
|
||||
0,
|
||||
"decrease_subdivision",
|
||||
""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Primitive Tool Modal Map");
|
||||
|
||||
/* This function is called for each space-type, only needs to add map once. */
|
||||
if (keymap && keymap->modal_items) {
|
||||
return;
|
||||
}
|
||||
|
||||
keymap = WM_modalkeymap_ensure(keyconf, "Primitive Tool Modal Map", modal_items);
|
||||
|
||||
WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_line");
|
||||
WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_polyline");
|
||||
WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_arc");
|
||||
WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_curve");
|
||||
WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_box");
|
||||
WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_primitive_circle");
|
||||
}
|
||||
@@ -7,12 +7,17 @@
|
||||
*/
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_numbers.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
@@ -714,4 +719,97 @@ IndexMask retrieve_editable_and_selected_elements(Object &object,
|
||||
return {};
|
||||
}
|
||||
|
||||
static float paint_calc_object_space_radius(ViewContext *vc,
|
||||
const float3 center,
|
||||
float pixel_radius)
|
||||
{
|
||||
Object *ob = vc->obact;
|
||||
const float2 xy_delta = float2(pixel_radius, 0.0f);
|
||||
const float4x4 mat = ob->object_to_world();
|
||||
|
||||
const float3 loc = math::transform_point(mat, center);
|
||||
|
||||
const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc);
|
||||
float3 delta;
|
||||
ED_view3d_win_to_delta(vc->region, xy_delta, zfac, delta);
|
||||
|
||||
const float scale = math::length(
|
||||
math::transform_direction(mat, float3(math::numbers::inv_sqrt3)));
|
||||
|
||||
return math::safe_divide(math::length(delta), scale);
|
||||
}
|
||||
|
||||
static float calc_brush_radius(ViewContext *vc,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const float3 location)
|
||||
{
|
||||
if (!BKE_brush_use_locked_size(scene, brush)) {
|
||||
return paint_calc_object_space_radius(vc, location, BKE_brush_size_get(scene, brush));
|
||||
}
|
||||
return BKE_brush_unprojected_radius_get(scene, brush);
|
||||
}
|
||||
|
||||
float radius_from_input_sample(const float pressure,
|
||||
const float3 location,
|
||||
ViewContext vc,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const BrushGpencilSettings *settings)
|
||||
{
|
||||
float radius = calc_brush_radius(&vc, brush, scene, location);
|
||||
if (BKE_brush_use_size_pressure(brush)) {
|
||||
radius *= BKE_curvemapping_evaluateF(settings->curve_sensitivity, 0, pressure);
|
||||
}
|
||||
return radius;
|
||||
}
|
||||
|
||||
float opacity_from_input_sample(const float pressure,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const BrushGpencilSettings *settings)
|
||||
{
|
||||
float opacity = BKE_brush_alpha_get(scene, brush);
|
||||
if (BKE_brush_use_alpha_pressure(brush)) {
|
||||
opacity *= BKE_curvemapping_evaluateF(settings->curve_strength, 0, pressure);
|
||||
}
|
||||
return opacity;
|
||||
}
|
||||
|
||||
int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op)
|
||||
{
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
const Object *object = CTX_data_active_object(C);
|
||||
if (!object || object->type != OB_GREASE_PENCIL) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
if (!grease_pencil.has_active_layer()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const Brush *brush = BKE_paint_brush_for_read(paint);
|
||||
if (brush == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
|
||||
|
||||
if (!active_layer.is_editable()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Ensure a drawing at the current keyframe. */
|
||||
if (!ed::greasepencil::ensure_active_keyframe(*scene, grease_pencil)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
@@ -29,6 +29,7 @@ struct Scene;
|
||||
struct UndoType;
|
||||
struct ViewDepths;
|
||||
struct View3D;
|
||||
struct ViewContext;
|
||||
namespace blender {
|
||||
namespace bke {
|
||||
enum class AttrDomain : int8_t;
|
||||
@@ -51,8 +52,10 @@ void ED_operatortypes_grease_pencil_layers();
|
||||
void ED_operatortypes_grease_pencil_select();
|
||||
void ED_operatortypes_grease_pencil_edit();
|
||||
void ED_operatortypes_grease_pencil_material();
|
||||
void ED_operatortypes_grease_pencil_primitives();
|
||||
void ED_operatormacros_grease_pencil();
|
||||
void ED_keymap_grease_pencil(wmKeyConfig *keyconf);
|
||||
void ED_primitivetool_modal_keymap(wmKeyConfig *keyconf);
|
||||
|
||||
void ED_undosys_type_grease_pencil(UndoType *undo_type);
|
||||
/**
|
||||
@@ -177,6 +180,18 @@ bool editable_grease_pencil_point_selection_poll(bContext *C);
|
||||
bool grease_pencil_painting_poll(bContext *C);
|
||||
bool grease_pencil_sculpting_poll(bContext *C);
|
||||
|
||||
float opacity_from_input_sample(const float pressure,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const BrushGpencilSettings *settings);
|
||||
float radius_from_input_sample(const float pressure,
|
||||
const float3 location,
|
||||
ViewContext vc,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const BrushGpencilSettings *settings);
|
||||
int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op);
|
||||
|
||||
struct DrawingInfo {
|
||||
const bke::greasepencil::Drawing &drawing;
|
||||
const int layer_index;
|
||||
|
||||
@@ -140,35 +140,9 @@ static bool grease_pencil_brush_stroke_test_start(bContext *C,
|
||||
|
||||
static int grease_pencil_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
const Object *object = CTX_data_active_object(C);
|
||||
if (!object || object->type != OB_GREASE_PENCIL) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
if (!grease_pencil.has_active_layer()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const Brush *brush = BKE_paint_brush_for_read(paint);
|
||||
if (brush == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
|
||||
|
||||
if (!active_layer.is_editable()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Ensure a drawing at the current keyframe. */
|
||||
if (!ed::greasepencil::ensure_active_keyframe(*scene, grease_pencil)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
|
||||
return OPERATOR_CANCELLED;
|
||||
int return_value = ed::greasepencil::grease_pencil_draw_operator_invoke(C, op);
|
||||
if (return_value != OPERATOR_RUNNING_MODAL) {
|
||||
return return_value;
|
||||
}
|
||||
|
||||
op->customdata = paint_stroke_new(C,
|
||||
@@ -180,7 +154,7 @@ static int grease_pencil_brush_stroke_invoke(bContext *C, wmOperator *op, const
|
||||
stroke_done,
|
||||
event->type);
|
||||
|
||||
const int return_value = op->type->modal(C, op, event);
|
||||
return_value = op->type->modal(C, op, event);
|
||||
if (return_value == OPERATOR_FINISHED) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "BLI_length_parameterize.hh"
|
||||
@@ -33,17 +34,6 @@ namespace blender::ed::sculpt_paint::greasepencil {
|
||||
static constexpr float POINT_OVERRIDE_THRESHOLD_PX = 3.0f;
|
||||
static constexpr float POINT_RESAMPLE_MIN_DISTANCE_PX = 10.0f;
|
||||
|
||||
static float calc_brush_radius(ViewContext *vc,
|
||||
const Brush *brush,
|
||||
const Scene *scene,
|
||||
const float3 location)
|
||||
{
|
||||
if (!BKE_brush_use_locked_size(scene, brush)) {
|
||||
return paint_calc_object_space_radius(vc, location, BKE_brush_size_get(scene, brush));
|
||||
}
|
||||
return BKE_brush_unprojected_radius_get(scene, brush);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline void linear_interpolation(const T &a, const T &b, MutableSpan<T> dst)
|
||||
{
|
||||
@@ -184,37 +174,23 @@ struct PaintOperationExecutor {
|
||||
BLI_assert(drawing_ != nullptr);
|
||||
}
|
||||
|
||||
float radius_from_input_sample(PaintOperation &self,
|
||||
const bContext &C,
|
||||
const InputSample &sample)
|
||||
{
|
||||
ViewContext vc = ED_view3d_viewcontext_init(const_cast<bContext *>(&C),
|
||||
CTX_data_depsgraph_pointer(&C));
|
||||
float radius = calc_brush_radius(
|
||||
&vc, brush_, scene_, self.placement_.project(sample.mouse_position));
|
||||
if (BKE_brush_use_size_pressure(brush_)) {
|
||||
radius *= BKE_curvemapping_evaluateF(settings_->curve_sensitivity, 0, sample.pressure);
|
||||
}
|
||||
return radius;
|
||||
}
|
||||
|
||||
float opacity_from_input_sample(const InputSample &sample)
|
||||
{
|
||||
float opacity = BKE_brush_alpha_get(scene_, brush_);
|
||||
if (BKE_brush_use_alpha_pressure(brush_)) {
|
||||
opacity *= BKE_curvemapping_evaluateF(settings_->curve_strength, 0, sample.pressure);
|
||||
}
|
||||
return opacity;
|
||||
}
|
||||
|
||||
void process_start_sample(PaintOperation &self,
|
||||
const bContext &C,
|
||||
const InputSample &start_sample,
|
||||
const int material_index)
|
||||
{
|
||||
const float2 start_coords = start_sample.mouse_position;
|
||||
const float start_radius = this->radius_from_input_sample(self, C, start_sample);
|
||||
const float start_opacity = this->opacity_from_input_sample(start_sample);
|
||||
ViewContext vc = ED_view3d_viewcontext_init(const_cast<bContext *>(&C),
|
||||
CTX_data_depsgraph_pointer(&C));
|
||||
const float start_radius = ed::greasepencil::radius_from_input_sample(
|
||||
start_sample.pressure,
|
||||
self.placement_.project(start_sample.mouse_position),
|
||||
vc,
|
||||
brush_,
|
||||
scene_,
|
||||
settings_);
|
||||
const float start_opacity = ed::greasepencil::opacity_from_input_sample(
|
||||
start_sample.pressure, brush_, scene_, settings_);
|
||||
const ColorGeometry4f start_vertex_color = ColorGeometry4f(vertex_color_);
|
||||
|
||||
self.screen_space_coords_orig_.append(start_coords);
|
||||
@@ -368,8 +344,17 @@ struct PaintOperationExecutor {
|
||||
const InputSample &extension_sample)
|
||||
{
|
||||
const float2 coords = extension_sample.mouse_position;
|
||||
const float radius = this->radius_from_input_sample(self, C, extension_sample);
|
||||
const float opacity = this->opacity_from_input_sample(extension_sample);
|
||||
ViewContext vc = ED_view3d_viewcontext_init(const_cast<bContext *>(&C),
|
||||
CTX_data_depsgraph_pointer(&C));
|
||||
const float radius = ed::greasepencil::radius_from_input_sample(
|
||||
extension_sample.pressure,
|
||||
self.placement_.project(extension_sample.mouse_position),
|
||||
vc,
|
||||
brush_,
|
||||
scene_,
|
||||
settings_);
|
||||
const float opacity = ed::greasepencil::opacity_from_input_sample(
|
||||
extension_sample.pressure, brush_, scene_, settings_);
|
||||
const ColorGeometry4f vertex_color = ColorGeometry4f(vertex_color_);
|
||||
|
||||
bke::CurvesGeometry &curves = drawing_->strokes_for_write();
|
||||
|
||||
Reference in New Issue
Block a user