Compare commits
148 Commits
tmp-workbe
...
GPencil_Ed
Author | SHA1 | Date | |
---|---|---|---|
ef2aecf2db | |||
cf8980559f | |||
7fd36af698 | |||
a72bbd9214 | |||
23656a54fd | |||
4213acedca | |||
34241fe6bb | |||
35db71ddb7 | |||
4bbc2cc1b1 | |||
ced987b5b4 | |||
d1a3223c76 | |||
c99dc5a1c1 | |||
833268e2b8 | |||
50fde02b42 | |||
85bbf7e04f | |||
418b908309 | |||
b3072b5617 | |||
fae7a8b97a | |||
0175834112 | |||
1806db07e7 | |||
9afd9dc78e | |||
ca8184c6cc | |||
5368505ef3 | |||
dda03a24b9 | |||
2167d521cc | |||
ca966644a8 | |||
1f93ec386b | |||
50111e83e7 | |||
2cdd34eb8c | |||
f501a4dd96 | |||
80b1620cf3 | |||
397f78f4be | |||
34d4c7c85f | |||
f4b8d2ef7a | |||
aacd18a73d | |||
4f2a3437a3 | |||
1b0f017a83 | |||
33b03e7c18 | |||
59a9e4027a | |||
6f6746fb01 | |||
e57fb9dae7 | |||
019a4ffcbe | |||
3e178d58c0 | |||
fcc58a6f94 | |||
163e434b32 | |||
f9b7ae71ff | |||
6cd2bfa3ff | |||
a8b143ec78 | |||
7743a2d839 | |||
035bd2736a | |||
b615ce637f | |||
0fb97b6b59 | |||
34993bf97d | |||
f49c833d00 | |||
dcc538c95a | |||
0da05bb4dc | |||
9ee297c61e | |||
a171fb4e3c | |||
7686a0ae9c | |||
488e5616dd | |||
b9029d698c | |||
affd1eacb5 | |||
422344d40a | |||
8a84465eab | |||
5fb8875f9b | |||
16ef4da5e6 | |||
b1c162074d | |||
ead5d92673 | |||
e04fd43d61 | |||
a4720696f1 | |||
5abaaf8311 | |||
029a6c2383 | |||
b81f3246f8 | |||
7d9bbf1b78 | |||
3d68c92f0a | |||
d9af8e9b41 | |||
763bc0c905 | |||
64c96f15fe | |||
558e33ca45 | |||
24201d81de | |||
4eeffd5ea9 | |||
1877525960 | |||
50b16fee19 | |||
6b1b0f78c0 | |||
56bd24c1a9 | |||
61b6eb606c | |||
eb22dcea61 | |||
3b1330396e | |||
95cf2fbc94 | |||
15b793bd84 | |||
5b02f2a0a2 | |||
f7250246d7 | |||
f32f83a619 | |||
3b569ac921 | |||
54fd4ec960 | |||
cea946112d | |||
f6135894fe | |||
b5b0968ba9 | |||
0ca69dbcdd | |||
9941da0702 | |||
73ffcb0754 | |||
352863b13f | |||
c78569a1e6 | |||
6726379ce3 | |||
b91ba21185 | |||
6cef173140 | |||
a84c9f527c | |||
dc1d95e404 | |||
f24f72afb9 | |||
2c178440e3 | |||
146b731d7c | |||
d1a8a15ff6 | |||
3148f82e27 | |||
db065e2ef8 | |||
dcbe481cfd | |||
6d5cfd1810 | |||
d4d3a77a2a | |||
7c2557c3da | |||
a5ed00f20a | |||
bce968044f | |||
32aff3021f | |||
74638921a6 | |||
df0fdc4178 | |||
78c92d7080 | |||
49e304c155 | |||
63d0a78624 | |||
0307ebe63b | |||
32c7b72ff2 | |||
61d9bcb26f | |||
571a46f32b | |||
718912f512 | |||
7e99bab425 | |||
7e333b863c | |||
85d3d4c96a | |||
ec4f82f9ca | |||
6a0547b934 | |||
d6e48b41ff | |||
043db44904 | |||
17f17a7013 | |||
8ae7128991 | |||
2013b63d29 | |||
e3654f1da5 | |||
dfb9df8578 | |||
44e171c7f0 | |||
3e67c06bd3 | |||
98d42b7236 | |||
786da62b4b | |||
70f84fe4c8 |
@@ -22,23 +22,34 @@
|
||||
from bpy.types import Menu, UIList
|
||||
|
||||
|
||||
def gpencil_stroke_placement_settings(context, layout, gpd):
|
||||
def gpencil_stroke_placement_settings(context, layout):
|
||||
if context.space_data.type == 'VIEW_3D':
|
||||
propname = "gpencil_stroke_placement_view3d"
|
||||
elif context.space_data.type == 'SEQUENCE_EDITOR':
|
||||
propname = "gpencil_stroke_placement_sequencer_preview"
|
||||
elif context.space_data.type == 'IMAGE_EDITOR':
|
||||
propname = "gpencil_stroke_placement_image_edit"
|
||||
else:
|
||||
propname = "gpencil_stroke_placement_view2d"
|
||||
|
||||
ts = context.tool_settings
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
||||
col.label(text="Stroke Placement:")
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop_enum(gpd, "draw_mode", 'VIEW')
|
||||
row.prop_enum(gpd, "draw_mode", 'CURSOR')
|
||||
row.prop_enum(ts, propname, 'VIEW')
|
||||
row.prop_enum(ts, propname, 'CURSOR')
|
||||
|
||||
if context.space_data.type == 'VIEW_3D':
|
||||
row = col.row(align=True)
|
||||
row.prop_enum(gpd, "draw_mode", 'SURFACE')
|
||||
row.prop_enum(gpd, "draw_mode", 'STROKE')
|
||||
row.prop_enum(ts, propname, 'SURFACE')
|
||||
row.prop_enum(ts, propname, 'STROKE')
|
||||
|
||||
row = col.row(align=False)
|
||||
row.active = gpd.draw_mode in {'SURFACE', 'STROKE'}
|
||||
row.prop(gpd, "use_stroke_endpoints")
|
||||
row.active = getattr(ts, propname) in {'SURFACE', 'STROKE'}
|
||||
row.prop(ts, "use_gpencil_stroke_endpoints")
|
||||
|
||||
|
||||
class GreasePencilDrawingToolsPanel:
|
||||
@@ -56,15 +67,19 @@ class GreasePencilDrawingToolsPanel:
|
||||
|
||||
col.label(text="Draw:")
|
||||
row = col.row(align=True)
|
||||
row.operator("gpencil.draw", text="Draw").mode = 'DRAW'
|
||||
row.operator("gpencil.draw", text="Erase").mode = 'ERASER'
|
||||
row.operator("gpencil.draw", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
|
||||
row.operator("gpencil.draw", icon='FORCE_CURVE', text="Erase").mode = 'ERASER' # XXX: Needs a dedicated icon
|
||||
|
||||
row = col.row(align=True)
|
||||
row.operator("gpencil.draw", text="Line").mode = 'DRAW_STRAIGHT'
|
||||
row.operator("gpencil.draw", text="Poly").mode = 'DRAW_POLY'
|
||||
row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
|
||||
row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(context.tool_settings, "use_grease_pencil_sessions", text="Continuous Drawing")
|
||||
sub = col.column(align=True)
|
||||
sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing")
|
||||
sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
|
||||
|
||||
col.separator()
|
||||
col.separator()
|
||||
|
||||
if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}:
|
||||
col.separator()
|
||||
@@ -74,11 +89,20 @@ class GreasePencilDrawingToolsPanel:
|
||||
row.prop(context.tool_settings, "grease_pencil_source", expand=True)
|
||||
elif context.space_data.type == 'CLIP_EDITOR':
|
||||
row.prop(context.space_data, "grease_pencil_source", expand=True)
|
||||
|
||||
col.separator()
|
||||
col.separator()
|
||||
|
||||
gpencil_stroke_placement_settings(context, col)
|
||||
|
||||
gpd = context.gpencil_data
|
||||
|
||||
if gpd:
|
||||
col.separator()
|
||||
gpencil_stroke_placement_settings(context, col, gpd)
|
||||
layout.separator()
|
||||
layout.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
|
||||
|
||||
if context.space_data.type == 'VIEW_3D':
|
||||
col.separator()
|
||||
@@ -95,67 +119,103 @@ class GreasePencilStrokeEditPanel:
|
||||
bl_label = "Edit Strokes"
|
||||
bl_category = "Grease Pencil"
|
||||
bl_region_type = 'TOOLS'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.gpencil_data is not None)
|
||||
if context.gpencil_data is None:
|
||||
return False
|
||||
|
||||
gpd = context.gpencil_data
|
||||
return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
gpd = context.gpencil_data
|
||||
edit_ok = bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
|
||||
layout.label(text="Select:")
|
||||
col = layout.column(align=True)
|
||||
col.operator("gpencil.select_all", text="Select All")
|
||||
col.operator("gpencil.select_border")
|
||||
col.operator("gpencil.select_circle")
|
||||
|
||||
layout.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
|
||||
col.operator("gpencil.select_linked")
|
||||
col.operator("gpencil.select_more")
|
||||
col.operator("gpencil.select_less")
|
||||
|
||||
col.separator()
|
||||
layout.separator()
|
||||
|
||||
col.label(text="Select:")
|
||||
subcol = col.column(align=True)
|
||||
subcol.active = edit_ok
|
||||
subcol.operator("gpencil.select_all", text="Select All")
|
||||
subcol.operator("gpencil.select_border")
|
||||
subcol.operator("gpencil.select_circle")
|
||||
|
||||
col.separator()
|
||||
|
||||
subcol = col.column(align=True)
|
||||
subcol.active = edit_ok
|
||||
subcol.operator("gpencil.select_linked")
|
||||
subcol.operator("gpencil.select_more")
|
||||
subcol.operator("gpencil.select_less")
|
||||
|
||||
col.separator()
|
||||
|
||||
col.label(text="Edit:")
|
||||
row = col.row(align=True)
|
||||
row.active = edit_ok
|
||||
layout.label(text="Edit:")
|
||||
row = layout.row(align=True)
|
||||
row.operator("gpencil.copy", text="Copy")
|
||||
row.operator("gpencil.paste", text="Paste")
|
||||
|
||||
subcol = col.column(align=True)
|
||||
subcol.active = edit_ok
|
||||
subcol.operator("gpencil.delete", text="Delete")
|
||||
subcol.operator("gpencil.duplicate_move", text="Duplicate")
|
||||
subcol.operator("transform.mirror", text="Mirror").gpencil_strokes = True
|
||||
col = layout.column(align=True)
|
||||
col.operator("gpencil.delete", text="Delete")
|
||||
col.operator("gpencil.duplicate_move", text="Duplicate")
|
||||
col.operator("transform.mirror", text="Mirror")
|
||||
|
||||
col.separator()
|
||||
layout.separator()
|
||||
|
||||
subcol = col.column(align=True)
|
||||
subcol.active = edit_ok
|
||||
subcol.operator("transform.translate").gpencil_strokes = True # icon='MAN_TRANS'
|
||||
subcol.operator("transform.rotate").gpencil_strokes = True # icon='MAN_ROT'
|
||||
subcol.operator("transform.resize", text="Scale").gpencil_strokes = True # icon='MAN_SCALE'
|
||||
col = layout.column(align=True)
|
||||
col.operator("transform.translate") # icon='MAN_TRANS'
|
||||
col.operator("transform.rotate") # icon='MAN_ROT'
|
||||
col.operator("transform.resize", text="Scale") # icon='MAN_SCALE'
|
||||
|
||||
col.separator()
|
||||
layout.separator()
|
||||
|
||||
subcol = col.column(align=True)
|
||||
subcol.active = edit_ok
|
||||
subcol.operator("transform.bend", text="Bend").gpencil_strokes = True
|
||||
subcol.operator("transform.shear", text="Shear").gpencil_strokes = True
|
||||
subcol.operator("transform.tosphere", text="To Sphere").gpencil_strokes = True
|
||||
col = layout.column(align=True)
|
||||
col.operator("transform.bend", text="Bend")
|
||||
col.operator("transform.shear", text="Shear")
|
||||
col.operator("transform.tosphere", text="To Sphere")
|
||||
|
||||
|
||||
class GreasePencilStrokeSculptPanel:
|
||||
# subclass must set
|
||||
# bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_label = "Sculpt Strokes"
|
||||
bl_category = "Grease Pencil"
|
||||
bl_region_type = 'TOOLS'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.gpencil_data is None:
|
||||
return False
|
||||
|
||||
gpd = context.gpencil_data
|
||||
return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
settings = context.tool_settings.gpencil_sculpt
|
||||
tool = settings.tool
|
||||
brush = settings.brush
|
||||
|
||||
layout.column().prop(settings, "tool", expand=True)
|
||||
|
||||
col = layout.column()
|
||||
col.prop(brush, "size", slider=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(brush, "strength", slider=True)
|
||||
row.prop(brush, "use_pressure_strength", text="")
|
||||
col.prop(brush, "use_falloff")
|
||||
|
||||
layout.separator()
|
||||
|
||||
if settings.tool in {'THICKNESS', 'PINCH', 'TWIST'}:
|
||||
layout.row().prop(brush, "direction", expand=True)
|
||||
|
||||
layout.separator()
|
||||
layout.prop(settings, "use_select_mask")
|
||||
|
||||
if settings.tool == 'SMOOTH':
|
||||
layout.prop(brush, "affect_pressure")
|
||||
|
||||
|
||||
###############################
|
||||
@@ -190,14 +250,14 @@ class GPENCIL_PIE_tool_palette(Menu):
|
||||
if gpd:
|
||||
if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
|
||||
# S - Exit Edit Mode
|
||||
pie.prop(gpd, "use_stroke_edit_mode", text="Exit Edit Mode", icon='EDIT')
|
||||
pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT')
|
||||
|
||||
# N - Transforms
|
||||
col = pie.column()
|
||||
row = col.row(align=True)
|
||||
row.operator("transform.translate", icon='MAN_TRANS').gpencil_strokes = True
|
||||
row.operator("transform.rotate", icon='MAN_ROT').gpencil_strokes = True
|
||||
row.operator("transform.resize", text="Scale", icon='MAN_SCALE').gpencil_strokes = True
|
||||
row.operator("transform.translate", icon='MAN_TRANS')
|
||||
row.operator("transform.rotate", icon='MAN_ROT')
|
||||
row.operator("transform.resize", text="Scale", icon='MAN_SCALE')
|
||||
row = col.row(align=True)
|
||||
row.label("Proportional Edit:")
|
||||
row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
|
||||
@@ -224,7 +284,7 @@ class GPENCIL_PIE_tool_palette(Menu):
|
||||
pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_PIE_tools_more"
|
||||
else:
|
||||
# Toggle Edit Mode
|
||||
pie.prop(gpd, "use_stroke_edit_mode", text="Enable Stroke Editing", icon='EDIT')
|
||||
pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT')
|
||||
|
||||
|
||||
class GPENCIL_PIE_settings_palette(Menu):
|
||||
@@ -261,11 +321,15 @@ class GPENCIL_PIE_settings_palette(Menu):
|
||||
col.prop(gpl, "use_onion_skinning")
|
||||
|
||||
# N - Active Layer
|
||||
# XXX: this should show an operator to change the active layer instead
|
||||
col = pie.column()
|
||||
col.label("Active Layer: ")
|
||||
col.prop(gpl, "info", text="")
|
||||
# col.prop(gpd, "layers")
|
||||
|
||||
row = col.row()
|
||||
row.operator_context = 'EXEC_REGION_WIN'
|
||||
row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL')
|
||||
row.prop(gpl, "info", text="")
|
||||
row.operator("gpencil.layer_remove", text="", icon='X')
|
||||
|
||||
row = col.row()
|
||||
row.prop(gpl, "lock")
|
||||
row.prop(gpl, "hide")
|
||||
@@ -294,17 +358,82 @@ class GPENCIL_PIE_tools_more(Menu):
|
||||
col.operator("gpencil.select_more", icon='ZOOMIN')
|
||||
col.operator("gpencil.select_less", icon='ZOOMOUT')
|
||||
|
||||
pie.operator("transform.mirror", icon='MOD_MIRROR').gpencil_strokes = True
|
||||
pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM').gpencil_strokes = True
|
||||
pie.operator("transform.shear", icon='MOD_TRIANGULATE').gpencil_strokes = True
|
||||
pie.operator("transform.tosphere", icon='MOD_MULTIRES').gpencil_strokes = True
|
||||
pie.operator("transform.mirror", icon='MOD_MIRROR')
|
||||
pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM')
|
||||
pie.operator("transform.shear", icon='MOD_TRIANGULATE')
|
||||
pie.operator("transform.tosphere", icon='MOD_MULTIRES')
|
||||
|
||||
pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
|
||||
pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette"
|
||||
|
||||
|
||||
class GPENCIL_PIE_sculpt(Menu):
|
||||
"""A pie menu for accessing Grease Pencil stroke sculpting settings"""
|
||||
bl_label = "Grease Pencil Sculpt"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
gpd = context.gpencil_data
|
||||
return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
|
||||
settings = context.tool_settings.gpencil_sculpt
|
||||
brush = settings.brush
|
||||
|
||||
# W - Launch Sculpt Mode
|
||||
col = pie.column()
|
||||
#col.label("Tool:")
|
||||
col.prop(settings, "tool", text="")
|
||||
col.operator("gpencil.brush_paint", text="Sculpt", icon='SCULPTMODE_HLT')
|
||||
|
||||
# E - Common Settings
|
||||
col = pie.column(align=True)
|
||||
col.prop(brush, "size", slider=True)
|
||||
row = col.row(align=True)
|
||||
row.prop(brush, "strength", slider=True)
|
||||
# row.prop(brush, "use_pressure_strength", text="", icon_only=True)
|
||||
col.prop(brush, "use_falloff")
|
||||
|
||||
# S - Change Brush Type Shortcuts
|
||||
row = pie.row()
|
||||
row.prop_enum(settings, "tool", value='GRAB')
|
||||
row.prop_enum(settings, "tool", value='PUSH')
|
||||
row.prop_enum(settings, "tool", value='CLONE')
|
||||
|
||||
# N - Change Brush Type Shortcuts
|
||||
row = pie.row()
|
||||
row.prop_enum(settings, "tool", value='SMOOTH')
|
||||
row.prop_enum(settings, "tool", value='THICKNESS')
|
||||
row.prop_enum(settings, "tool", value='RANDOMISE')
|
||||
|
||||
|
||||
###############################
|
||||
|
||||
|
||||
class GPENCIL_MT_snap(Menu):
|
||||
bl_label = "Snap"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
|
||||
layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
|
||||
layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Offset)").use_offset = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
|
||||
layout.operator("view3d.snap_cursor_to_center", text="Cursor to Center")
|
||||
layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
|
||||
|
||||
|
||||
###############################
|
||||
|
||||
|
||||
class GPENCIL_UL_layer(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
# assert(isinstance(item, bpy.types.GPencilLayer)
|
||||
@@ -328,6 +457,25 @@ class GPENCIL_UL_layer(UIList):
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
class GPENCIL_MT_layer_specials(Menu):
|
||||
bl_label = "Layer"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("gpencil.layer_duplicate", icon='COPY_ID') # XXX: needs a dedicated icon
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
|
||||
layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
|
||||
layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
|
||||
|
||||
|
||||
class GreasePencilDataPanel:
|
||||
# subclass must set
|
||||
# bl_space_type = 'IMAGE_EDITOR'
|
||||
@@ -379,7 +527,7 @@ class GreasePencilDataPanel:
|
||||
|
||||
gpl = context.active_gpencil_layer
|
||||
if gpl:
|
||||
sub.operator("gpencil.layer_duplicate", icon='COPY_ID', text="") # XXX: needs a dedicated icon
|
||||
sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if len(gpd.layers) > 1:
|
||||
col.separator()
|
||||
@@ -388,6 +536,12 @@ class GreasePencilDataPanel:
|
||||
sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
|
||||
sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
|
||||
|
||||
col.separator()
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.operator("gpencil.layer_isolate", icon='SOLO_OFF', text="").affect_visibility = False
|
||||
sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
|
||||
|
||||
if gpl:
|
||||
self.draw_layer(layout, gpl)
|
||||
|
||||
@@ -492,4 +646,4 @@ class GreasePencilToolsPanel:
|
||||
layout.separator()
|
||||
layout.separator()
|
||||
|
||||
gpencil_stroke_placement_settings(context, layout, gpd)
|
||||
gpencil_stroke_placement_settings(context, layout)
|
||||
|
@@ -24,6 +24,7 @@ from bpy.app.translations import pgettext_iface as iface_
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel,
|
||||
GreasePencilStrokeSculptPanel,
|
||||
GreasePencilDataPanel
|
||||
)
|
||||
|
||||
@@ -1134,6 +1135,10 @@ class CLIP_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
|
||||
class CLIP_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
|
||||
# Grease Pencil stroke sculpting tools
|
||||
class CLIP_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
|
||||
|
||||
class CLIP_MT_view(Menu):
|
||||
bl_label = "View"
|
||||
|
@@ -138,6 +138,19 @@ class DOPESHEET_HT_header(Header):
|
||||
# 'genericFiltersOnly' limits the options to only the relevant 'generic' subset of
|
||||
# filters which will work here and are useful (especially for character animation)
|
||||
dopesheet_filter(layout, context, genericFiltersOnly=True)
|
||||
elif st.mode == 'GPENCIL':
|
||||
row = layout.row(align=True)
|
||||
row.prop(st.dopesheet, "show_gpencil_3d_only", text="Active Only")
|
||||
|
||||
if st.dopesheet.show_gpencil_3d_only:
|
||||
row = layout.row(align=True)
|
||||
row.prop(st.dopesheet, "show_only_selected", text="")
|
||||
row.prop(st.dopesheet, "show_hidden", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(st.dopesheet, "use_filter_text", text="")
|
||||
if st.dopesheet.use_filter_text:
|
||||
row.prop(st.dopesheet, "filter_text", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(toolsettings, "use_proportional_action",
|
||||
|
@@ -28,6 +28,7 @@ from bl_ui.properties_paint_common import (
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel,
|
||||
GreasePencilStrokeSculptPanel,
|
||||
GreasePencilDataPanel,
|
||||
)
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
@@ -1192,5 +1193,10 @@ class IMAGE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
|
||||
|
||||
# Grease Pencil stroke sculpting tools
|
||||
class IMAGE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
bpy.utils.register_module(__name__)
|
||||
|
@@ -24,6 +24,7 @@ from bpy.app.translations import pgettext_iface as iface_
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel,
|
||||
GreasePencilStrokeSculptPanel,
|
||||
GreasePencilDataPanel,
|
||||
GreasePencilToolsPanel,
|
||||
)
|
||||
@@ -488,6 +489,11 @@ class NODE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'TOOLS'
|
||||
|
||||
# Grease Pencil stroke sculpting tools
|
||||
class NODE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'TOOLS'
|
||||
|
||||
# -----------------------------
|
||||
|
||||
|
||||
|
@@ -118,6 +118,14 @@ class VIEW3D_HT_header(Header):
|
||||
row.operator("pose.paste", text="", icon='PASTEDOWN').flipped = False
|
||||
row.operator("pose.paste", text="", icon='PASTEFLIPDOWN').flipped = True
|
||||
|
||||
# GPencil
|
||||
if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode:
|
||||
row = layout.row(align=True)
|
||||
row.operator("gpencil.copy", text="", icon='COPYDOWN')
|
||||
row.operator("gpencil.paste", text="", icon='PASTEDOWN')
|
||||
|
||||
layout.prop(context.gpencil_data, "use_onion_skinning", text="Onion Skins", icon='PARTICLE_PATH') # XXX: icon
|
||||
|
||||
|
||||
class VIEW3D_MT_editor_menus(Menu):
|
||||
bl_space_type = 'VIEW3D_MT_editor_menus'
|
||||
@@ -131,11 +139,14 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
obj = context.active_object
|
||||
mode_string = context.mode
|
||||
edit_object = context.edit_object
|
||||
gp_edit = context.gpencil_data and context.gpencil_data.use_stroke_edit_mode
|
||||
|
||||
layout.menu("VIEW3D_MT_view")
|
||||
|
||||
# Select Menu
|
||||
if mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}:
|
||||
if gp_edit:
|
||||
layout.menu("VIEW3D_MT_select_gpencil")
|
||||
elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}:
|
||||
mesh = obj.data
|
||||
if mesh.use_paint_mask:
|
||||
layout.menu("VIEW3D_MT_select_paint_mask")
|
||||
@@ -144,7 +155,9 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
elif mode_string != 'SCULPT':
|
||||
layout.menu("VIEW3D_MT_select_%s" % mode_string.lower())
|
||||
|
||||
if mode_string == 'OBJECT':
|
||||
if gp_edit:
|
||||
pass
|
||||
elif mode_string == 'OBJECT':
|
||||
layout.menu("INFO_MT_add", text="Add")
|
||||
elif mode_string == 'EDIT_MESH':
|
||||
layout.menu("INFO_MT_mesh_add", text="Add")
|
||||
@@ -157,7 +170,9 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
elif mode_string == 'EDIT_ARMATURE':
|
||||
layout.menu("INFO_MT_edit_armature_add", text="Add")
|
||||
|
||||
if edit_object:
|
||||
if gp_edit:
|
||||
layout.menu("VIEW3D_MT_edit_gpencil")
|
||||
elif edit_object:
|
||||
layout.menu("VIEW3D_MT_edit_%s" % edit_object.type.lower())
|
||||
elif obj:
|
||||
if mode_string != 'PAINT_TEXTURE':
|
||||
@@ -883,6 +898,27 @@ class VIEW3D_MT_select_edit_armature(Menu):
|
||||
layout.operator("object.select_pattern", text="Select Pattern...")
|
||||
|
||||
|
||||
class VIEW3D_MT_select_gpencil(Menu):
|
||||
bl_label = "Select"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("gpencil.select_border")
|
||||
layout.operator("gpencil.select_circle")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.select_all", text="(De)select All").action = 'TOGGLE'
|
||||
layout.operator("gpencil.select_all", text="Inverse").action = 'INVERT'
|
||||
layout.operator("gpencil.select_linked", text="Linked")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.select_more")
|
||||
layout.operator("gpencil.select_less")
|
||||
|
||||
|
||||
class VIEW3D_MT_select_paint_mask(Menu):
|
||||
bl_label = "Select"
|
||||
|
||||
@@ -2822,6 +2858,81 @@ class VIEW3D_MT_edit_armature_delete(Menu):
|
||||
layout.operator("armature.dissolve", text="Dissolve")
|
||||
|
||||
|
||||
# ********** GPencil Stroke Edit menu **********
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_gpencil(Menu):
|
||||
bl_label = "GPencil"
|
||||
|
||||
def draw(self, context):
|
||||
toolsettings = context.tool_settings
|
||||
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("ed.undo")
|
||||
layout.operator("ed.redo")
|
||||
layout.operator("ed.undo_history")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.brush_paint", text="Sculpt Strokes").wait_for_input = True
|
||||
layout.prop_menu_enum(toolsettings.gpencil_sculpt, "tool", text="Sculpt Brush")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_edit_gpencil_transform")
|
||||
layout.operator("transform.mirror", text="Mirror")
|
||||
layout.menu("GPENCIL_MT_snap")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_object_animation") # NOTE: provides keyingset access...
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_edit_gpencil_delete")
|
||||
layout.operator("gpencil.duplicate_move", text="Duplicate")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.copy", text="Copy")
|
||||
layout.operator("gpencil.paste", text="Paste")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.prop_menu_enum(toolsettings, "proportional_edit")
|
||||
layout.prop_menu_enum(toolsettings, "proportional_edit_falloff")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("gpencil.reveal")
|
||||
layout.operator("gpencil.hide", text="Show Active Layer Only").unselected = True
|
||||
layout.operator("gpencil.hide", text="Hide Active Layer").unselected = False
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator_menu_enum("gpencil.move_to_layer", "layer", text="Move to Layer")
|
||||
layout.operator_menu_enum("gpencil.convert", "type", text="Convert to Geometry...")
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_gpencil_transform(Menu):
|
||||
bl_label = "Transform"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("transform.translate")
|
||||
layout.operator("transform.rotate")
|
||||
layout.operator("transform.resize", text="Scale")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("transform.bend", text="Bend")
|
||||
layout.operator("transform.shear", text="Shear")
|
||||
layout.operator("transform.tosphere", text="To Sphere")
|
||||
layout.operator("transform.transform", text="Shrink Fatten").mode = 'GPENCIL_SHRINKFATTEN'
|
||||
|
||||
|
||||
# ********** Panel **********
|
||||
|
||||
|
||||
|
@@ -21,7 +21,8 @@ import bpy
|
||||
from bpy.types import Menu, Panel, UIList
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel
|
||||
GreasePencilStrokeEditPanel,
|
||||
GreasePencilStrokeSculptPanel
|
||||
)
|
||||
from bl_ui.properties_paint_common import (
|
||||
UnifiedPaintPanel,
|
||||
@@ -1875,6 +1876,11 @@ class VIEW3D_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
|
||||
|
||||
# Grease Pencil stroke sculpting tools
|
||||
class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
|
||||
|
||||
# Note: moved here so that it's always in last position in 'Tools' panels!
|
||||
class VIEW3D_PT_tools_history(View3DPanel, Panel):
|
||||
bl_category = "Tools"
|
||||
|
@@ -47,6 +47,7 @@ void BKE_gpencil_free(struct bGPdata *gpd);
|
||||
void gpencil_stroke_sync_selection(struct bGPDstroke *gps);
|
||||
|
||||
struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
|
||||
struct bGPDframe *gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
|
||||
struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd, const char *name, int setactive);
|
||||
struct bGPdata *gpencil_data_addnew(const char name[]);
|
||||
|
||||
@@ -56,9 +57,24 @@ struct bGPdata *gpencil_data_duplicate(struct bGPdata *gpd, bool internal_copy);
|
||||
|
||||
void gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf);
|
||||
|
||||
|
||||
/* How gpencil_layer_getframe() should behave when there
|
||||
* is no existing GP-Frame on the frame requested.
|
||||
*/
|
||||
typedef enum eGP_GetFrame_Mode {
|
||||
/* Use the preceeding gp-frame (i.e. don't add anything) */
|
||||
GP_GETFRAME_USE_PREV = 0,
|
||||
|
||||
/* Add a new empty/blank frame */
|
||||
GP_GETFRAME_ADD_NEW = 1,
|
||||
/* Make a copy of the active frame */
|
||||
GP_GETFRAME_ADD_COPY = 2
|
||||
} eGP_GetFrame_Mode;
|
||||
|
||||
struct bGPDframe *gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew);
|
||||
struct bGPDframe *BKE_gpencil_layer_find_frame(struct bGPDlayer *gpl, int cframe);
|
||||
struct bGPDframe *gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, short addnew);
|
||||
bool gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf);
|
||||
|
||||
struct bGPDlayer *gpencil_layer_getactive(struct bGPdata *gpd);
|
||||
void gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active);
|
||||
void gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl);
|
||||
|
@@ -132,7 +132,7 @@ bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe)
|
||||
bGPDframe *gpf = NULL, *gf = NULL;
|
||||
short state = 0;
|
||||
|
||||
/* error checking (neg frame only if they are not allowed in Blender!) */
|
||||
/* error checking */
|
||||
if (gpl == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -178,6 +178,61 @@ bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe)
|
||||
return gpf;
|
||||
}
|
||||
|
||||
/* add a copy of the active gp-frame to the given layer */
|
||||
bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
|
||||
{
|
||||
bGPDframe *new_frame, *gpf;
|
||||
bool found = false;
|
||||
|
||||
/* Error checking/handling */
|
||||
if (gpl == NULL) {
|
||||
/* no layer */
|
||||
return NULL;
|
||||
}
|
||||
else if (gpl->actframe == NULL) {
|
||||
/* no active frame, so just create a new one from scratch */
|
||||
return gpencil_frame_addnew(gpl, cframe);
|
||||
}
|
||||
|
||||
/* Create a copy of the frame */
|
||||
new_frame = gpencil_frame_duplicate(gpl->actframe);
|
||||
|
||||
/* Find frame to insert it before */
|
||||
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||
if (gpf->framenum > cframe) {
|
||||
/* Add it here */
|
||||
BLI_insertlinkbefore(&gpl->frames, gpf, new_frame);
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else if (gpf->framenum == cframe) {
|
||||
/* This only happens when we're editing with framelock on...
|
||||
* - Delete the new frame and don't do anything else here...
|
||||
*/
|
||||
free_gpencil_strokes(new_frame);
|
||||
MEM_freeN(new_frame);
|
||||
new_frame = NULL;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == false) {
|
||||
/* Add new frame to the end */
|
||||
BLI_addtail(&gpl->frames, new_frame);
|
||||
}
|
||||
|
||||
/* Ensure that frame is set up correctly, and return it */
|
||||
if (new_frame) {
|
||||
new_frame->framenum = cframe;
|
||||
gpl->actframe = new_frame;
|
||||
}
|
||||
|
||||
return new_frame;
|
||||
}
|
||||
|
||||
/* add a new gp-layer and make it the active layer */
|
||||
bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, int setactive)
|
||||
{
|
||||
@@ -197,6 +252,13 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, int setactive)
|
||||
copy_v4_v4(gpl->color, U.gpencil_new_layer_col);
|
||||
gpl->thickness = 3;
|
||||
|
||||
/* onion-skinning settings */
|
||||
gpl->flag |= (GP_LAYER_GHOST_PREVCOL | GP_LAYER_GHOST_NEXTCOL);
|
||||
|
||||
ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */
|
||||
ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */
|
||||
|
||||
|
||||
/* auto-name */
|
||||
BLI_strncpy(gpl->info, name, sizeof(gpl->info));
|
||||
BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info));
|
||||
@@ -387,7 +449,7 @@ bGPDframe *BKE_gpencil_layer_find_frame(bGPDlayer *gpl, int cframe)
|
||||
* - this sets the layer's actframe var (if allowed to)
|
||||
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
|
||||
*/
|
||||
bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, short addnew)
|
||||
bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
|
||||
{
|
||||
bGPDframe *gpf = NULL;
|
||||
short found = 0;
|
||||
@@ -425,6 +487,8 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, short addnew)
|
||||
if (addnew) {
|
||||
if ((found) && (gpf->framenum == cframe))
|
||||
gpl->actframe = gpf;
|
||||
else if (addnew == GP_GETFRAME_ADD_COPY)
|
||||
gpl->actframe = gpencil_frame_addcopy(gpl, cframe);
|
||||
else
|
||||
gpl->actframe = gpencil_frame_addnew(gpl, cframe);
|
||||
}
|
||||
@@ -445,6 +509,8 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, short addnew)
|
||||
if (addnew) {
|
||||
if ((found) && (gpf->framenum == cframe))
|
||||
gpl->actframe = gpf;
|
||||
else if (addnew == GP_GETFRAME_ADD_COPY)
|
||||
gpl->actframe = gpencil_frame_addcopy(gpl, cframe);
|
||||
else
|
||||
gpl->actframe = gpencil_frame_addnew(gpl, cframe);
|
||||
}
|
||||
|
@@ -750,6 +750,53 @@ void BKE_scene_init(Scene *sce)
|
||||
copy_v2_fl2(sce->safe_areas.action_center, 15.0f / 100.0f, 5.0f / 100.0f);
|
||||
|
||||
sce->preview = NULL;
|
||||
|
||||
/* GP Sculpt brushes */
|
||||
{
|
||||
GP_BrushEdit_Settings *gset = &sce->toolsettings->gp_sculpt;
|
||||
GP_EditBrush_Data *gp_brush;
|
||||
|
||||
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_SMOOTH];
|
||||
gp_brush->size = 25;
|
||||
gp_brush->strength = 0.3f;
|
||||
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE;
|
||||
|
||||
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_THICKNESS];
|
||||
gp_brush->size = 25;
|
||||
gp_brush->strength = 0.5f;
|
||||
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB];
|
||||
gp_brush->size = 50;
|
||||
gp_brush->strength = 0.3f;
|
||||
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PUSH];
|
||||
gp_brush->size = 25;
|
||||
gp_brush->strength = 0.3f;
|
||||
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_TWIST];
|
||||
gp_brush->size = 50;
|
||||
gp_brush->strength = 0.3f; // XXX?
|
||||
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PINCH];
|
||||
gp_brush->size = 50;
|
||||
gp_brush->strength = 0.5f; // XXX?
|
||||
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_RANDOMISE];
|
||||
gp_brush->size = 25;
|
||||
gp_brush->strength = 0.5f;
|
||||
gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
}
|
||||
|
||||
/* GP Stroke Placement */
|
||||
sce->toolsettings->gpencil_v3d_align = GP_PROJECT_VIEWSPACE;
|
||||
sce->toolsettings->gpencil_v2d_align = GP_PROJECT_VIEWSPACE;
|
||||
sce->toolsettings->gpencil_seq_align = GP_PROJECT_VIEWSPACE;
|
||||
sce->toolsettings->gpencil_ima_align = GP_PROJECT_VIEWSPACE;
|
||||
}
|
||||
|
||||
Scene *BKE_scene_add(Main *bmain, const char *name)
|
||||
|
@@ -5822,6 +5822,7 @@ static void direct_link_scene(FileData *fd, Scene *sce)
|
||||
sce->toolsettings->particle.paintcursor = NULL;
|
||||
sce->toolsettings->particle.scene = NULL;
|
||||
sce->toolsettings->particle.object = NULL;
|
||||
sce->toolsettings->gp_sculpt.paintcursor = NULL;
|
||||
|
||||
/* in rare cases this is needed, see [#33806] */
|
||||
if (sce->toolsettings->vpaint) {
|
||||
|
@@ -38,6 +38,7 @@
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_cloth_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_sdna_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
@@ -935,5 +936,81 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
Scene *scene;
|
||||
for (scene = main->scene.first; scene; scene = scene->id.next) {
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
if (ts->gp_sculpt.brush[0].size == 0) {
|
||||
GP_BrushEdit_Settings *gset = &ts->gp_sculpt;
|
||||
GP_EditBrush_Data *brush;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_SMOOTH];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.3f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_THICKNESS];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.5f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB];
|
||||
brush->size = 50;
|
||||
brush->strength = 0.3f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_PUSH];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.3f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_TWIST];
|
||||
brush->size = 50;
|
||||
brush->strength = 0.3f; // XXX?
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_PINCH];
|
||||
brush->size = 50;
|
||||
brush->strength = 0.5f; // XXX?
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_RANDOMISE];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.5f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_CLONE];
|
||||
brush->size = 50;
|
||||
brush->strength = 1.0f;
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "gpencil_v3d_align")) {
|
||||
#if 0 /* XXX: Cannot do this, as we get random crashes... */
|
||||
if (scene->gpd) {
|
||||
bGPdata *gpd = scene->gpd;
|
||||
|
||||
/* Copy over the settings stored in the GP datablock linked to the scene, for minimal disruption */
|
||||
ts->gpencil_v3d_align = 0;
|
||||
|
||||
if (gpd->flag & GP_DATA_VIEWALIGN) ts->gpencil_v3d_align |= GP_PROJECT_VIEWSPACE;
|
||||
if (gpd->flag & GP_DATA_DEPTH_VIEW) ts->gpencil_v3d_align |= GP_PROJECT_DEPTH_VIEW;
|
||||
if (gpd->flag & GP_DATA_DEPTH_STROKE) ts->gpencil_v3d_align |= GP_PROJECT_DEPTH_STROKE;
|
||||
|
||||
if (gpd->flag & GP_DATA_DEPTH_STROKE_ENDPOINTS)
|
||||
ts->gpencil_v3d_align |= GP_PROJECT_DEPTH_STROKE_ENDPOINTS;
|
||||
}
|
||||
else {
|
||||
/* Default to cursor for all standard 3D views */
|
||||
ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE;
|
||||
}
|
||||
#endif
|
||||
|
||||
ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE;
|
||||
ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE;
|
||||
ts->gpencil_seq_align = GP_PROJECT_VIEWSPACE;
|
||||
ts->gpencil_ima_align = GP_PROJECT_VIEWSPACE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -91,6 +91,51 @@ void BLO_update_defaults_startup_blend(Main *bmain)
|
||||
sculpt->flags |= SCULPT_DYNTOPO_COLLAPSE;
|
||||
sculpt->detail_size = 12;
|
||||
}
|
||||
|
||||
if (ts->gp_sculpt.brush[0].size == 0) {
|
||||
GP_BrushEdit_Settings *gset = &ts->gp_sculpt;
|
||||
GP_EditBrush_Data *brush;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_SMOOTH];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.3f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_THICKNESS];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.5f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB];
|
||||
brush->size = 50;
|
||||
brush->strength = 0.3f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_PUSH];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.3f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_TWIST];
|
||||
brush->size = 50;
|
||||
brush->strength = 0.3f; // XXX?
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_PINCH];
|
||||
brush->size = 50;
|
||||
brush->strength = 0.5f; // XXX?
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
|
||||
brush = &gset->brush[GP_EDITBRUSH_TYPE_RANDOMISE];
|
||||
brush->size = 25;
|
||||
brush->strength = 0.5f;
|
||||
brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF;
|
||||
}
|
||||
|
||||
ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE;
|
||||
ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE;
|
||||
ts->gpencil_seq_align = GP_PROJECT_VIEWSPACE;
|
||||
ts->gpencil_ima_align = GP_PROJECT_VIEWSPACE;
|
||||
}
|
||||
|
||||
scene->gm.lodflag |= SCE_LOD_USE_HYST;
|
||||
|
@@ -403,7 +403,6 @@ static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prev
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
Mask *mask = CTX_data_edit_mask(C);
|
||||
bDopeSheet ads = {NULL};
|
||||
DLRBT_Tree keys;
|
||||
@@ -425,11 +424,12 @@ static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prev
|
||||
|
||||
/* populate tree with keyframe nodes */
|
||||
scene_to_keylist(&ads, scene, &keys, NULL);
|
||||
gpencil_to_keylist(&ads, scene->gpd, &keys);
|
||||
|
||||
if (ob)
|
||||
if (ob) {
|
||||
ob_to_keylist(&ads, ob, &keys, NULL);
|
||||
|
||||
gpencil_to_keylist(&ads, gpd, &keys);
|
||||
gpencil_to_keylist(&ads, ob->gpd, &keys);
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
MaskLayer *masklay = BKE_mask_layer_active(mask);
|
||||
|
@@ -1505,7 +1505,7 @@ static size_t animdata_filter_shapekey(bAnimContext *ac, ListBase *anim_data, Ke
|
||||
}
|
||||
|
||||
/* Helper for Grease Pencil - layers within a datablock */
|
||||
static size_t animdata_filter_gpencil_data(ListBase *anim_data, bGPdata *gpd, int filter_mode)
|
||||
static size_t animdata_filter_gpencil_layers_data(ListBase *anim_data, bDopeSheet *ads, bGPdata *gpd, int filter_mode)
|
||||
{
|
||||
bGPDlayer *gpl;
|
||||
size_t items = 0;
|
||||
@@ -1518,6 +1518,13 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data, bGPdata *gpd, in
|
||||
if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_GPL(gpl)) {
|
||||
/* active... */
|
||||
if (!(filter_mode & ANIMFILTER_ACTIVE) || (gpl->flag & GP_LAYER_ACTIVE)) {
|
||||
/* skip layer if the name doesn't match the filter string */
|
||||
if ((ads) && (ads->filterflag & ADS_FILTER_BY_FCU_NAME)) {
|
||||
if (BLI_strcasestr(gpl->info, ads->searchstr) == NULL)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* add to list */
|
||||
ANIMCHANNEL_NEW_CHANNEL(gpl, ANIMTYPE_GPLAYER, gpd);
|
||||
}
|
||||
@@ -1528,54 +1535,121 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data, bGPdata *gpd, in
|
||||
return items;
|
||||
}
|
||||
|
||||
/* Grab all Grease Pencil datablocks in file */
|
||||
// TODO: should this be amalgamated with the dopesheet filtering code?
|
||||
static size_t animdata_filter_gpencil(ListBase *anim_data, void *UNUSED(data), int filter_mode)
|
||||
/* Helper for Grease Pencil - Grease Pencil datablock - GP Frames */
|
||||
static size_t animdata_filter_gpencil_data(ListBase *anim_data, bDopeSheet *ads, bGPdata *gpd, int filter_mode)
|
||||
{
|
||||
bGPdata *gpd;
|
||||
size_t items = 0;
|
||||
|
||||
/* for now, grab grease pencil datablocks directly from main */
|
||||
// XXX: this is not good...
|
||||
for (gpd = G.main->gpencil.first; gpd; gpd = gpd->id.next) {
|
||||
/* When asked from "AnimData" blocks (i.e. the top-level containers for normal animation),
|
||||
* for convenience, this will return GP Datablocks instead. This may cause issues down
|
||||
* the track, but for now, this will do...
|
||||
*/
|
||||
if (filter_mode & ANIMFILTER_ANIMDATA) {
|
||||
/* just add GPD as a channel - this will add everything needed */
|
||||
ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL);
|
||||
}
|
||||
else {
|
||||
ListBase tmp_data = {NULL, NULL};
|
||||
size_t tmp_items = 0;
|
||||
|
||||
/* only show if gpd is used by something... */
|
||||
if (ID_REAL_USERS(gpd) < 1)
|
||||
continue;
|
||||
|
||||
/* When asked from "AnimData" blocks (i.e. the top-level containers for normal animation),
|
||||
* for convenience, this will return GP Datablocks instead. This may cause issues down
|
||||
* the track, but for now, this will do...
|
||||
*/
|
||||
if (filter_mode & ANIMFILTER_ANIMDATA) {
|
||||
/* just add GPD as a channel - this will add everything needed */
|
||||
ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL);
|
||||
/* add gpencil animation channels */
|
||||
BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_GPD(gpd))
|
||||
{
|
||||
tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode);
|
||||
}
|
||||
else {
|
||||
/* add gpencil animation channels */
|
||||
BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_GPD(gpd))
|
||||
{
|
||||
tmp_items += animdata_filter_gpencil_data(&tmp_data, gpd, filter_mode);
|
||||
END_ANIMFILTER_SUBCHANNELS;
|
||||
|
||||
/* did we find anything? */
|
||||
if (tmp_items) {
|
||||
/* include data-expand widget first */
|
||||
if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
|
||||
/* add gpd as channel too (if for drawing, and it has layers) */
|
||||
ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL);
|
||||
}
|
||||
END_ANIMFILTER_SUBCHANNELS;
|
||||
|
||||
/* did we find anything? */
|
||||
if (tmp_items) {
|
||||
/* include data-expand widget first */
|
||||
if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
|
||||
/* add gpd as channel too (if for drawing, and it has layers) */
|
||||
ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL);
|
||||
/* now add the list of collected channels */
|
||||
BLI_movelisttolist(anim_data, &tmp_data);
|
||||
BLI_assert(BLI_listbase_is_empty(&tmp_data));
|
||||
items += tmp_items;
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/* Grab all Grease Pencil datablocks in file */
|
||||
// TODO: should this be amalgamated with the dopesheet filtering code?
|
||||
static size_t animdata_filter_gpencil(bAnimContext *ac, ListBase *anim_data, void *UNUSED(data), int filter_mode)
|
||||
{
|
||||
bDopeSheet *ads = ac->ads;
|
||||
size_t items = 0;
|
||||
|
||||
if (ads->filterflag & ADS_FILTER_GP_3DONLY) {
|
||||
Scene *scene = (Scene *)ads->source;
|
||||
Base *base;
|
||||
|
||||
/* Active scene's GPencil block first - No parent item needed... */
|
||||
if (scene->gpd) {
|
||||
items += animdata_filter_gpencil_data(anim_data, ads, scene->gpd, filter_mode);
|
||||
}
|
||||
|
||||
/* Objects in the scene */
|
||||
for (base = scene->base.first; base; base = base->next) {
|
||||
/* Only consider this object if it has got some GP data (saving on all the other tests) */
|
||||
if (base->object && base->object->gpd) {
|
||||
Object *ob = base->object;
|
||||
|
||||
/* firstly, check if object can be included, by the following factors:
|
||||
* - if only visible, must check for layer and also viewport visibility
|
||||
* --> while tools may demand only visible, user setting takes priority
|
||||
* as user option controls whether sets of channels get included while
|
||||
* tool-flag takes into account collapsed/open channels too
|
||||
* - if only selected, must check if object is selected
|
||||
* - there must be animation data to edit (this is done recursively as we
|
||||
* try to add the channels)
|
||||
*/
|
||||
if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
|
||||
/* layer visibility - we check both object and base, since these may not be in sync yet */
|
||||
if ((scene->lay & (ob->lay | base->lay)) == 0) continue;
|
||||
|
||||
/* outliner restrict-flag */
|
||||
if (ob->restrictflag & OB_RESTRICT_VIEW) continue;
|
||||
}
|
||||
|
||||
/* now add the list of collected channels */
|
||||
BLI_movelisttolist(anim_data, &tmp_data);
|
||||
BLI_assert(BLI_listbase_is_empty(&tmp_data));
|
||||
items += tmp_items;
|
||||
/* check selection and object type filters */
|
||||
if ( (ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == scene->basact)*/) ) {
|
||||
/* only selected should be shown */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check if object belongs to the filtering group if option to filter
|
||||
* objects by the grouped status is on
|
||||
* - used to ease the process of doing multiple-character choreographies
|
||||
*/
|
||||
if (ads->filterflag & ADS_FILTER_ONLYOBGROUP) {
|
||||
if (BKE_group_object_exists(ads->filter_grp, ob) == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* finally, include this object's grease pencil datablock */
|
||||
/* XXX: Should we store these under expanders per item? */
|
||||
items += animdata_filter_gpencil_data(anim_data, ads, ob->gpd, filter_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
bGPdata *gpd;
|
||||
|
||||
/* Grab all Grease Pencil datablocks directly from main, but only those that seem to be useful somewhere */
|
||||
for (gpd = G.main->gpencil.first; gpd; gpd = gpd->id.next) {
|
||||
/* only show if gpd is used by something... */
|
||||
if (ID_REAL_USERS(gpd) < 1)
|
||||
continue;
|
||||
|
||||
/* add GP frames from this datablock */
|
||||
items += animdata_filter_gpencil_data(anim_data, ads, gpd, filter_mode);
|
||||
}
|
||||
}
|
||||
|
||||
/* return the number of items added to the list */
|
||||
return items;
|
||||
@@ -2880,7 +2954,7 @@ size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, eAnimFilter_F
|
||||
case ANIMCONT_GPENCIL:
|
||||
{
|
||||
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items))
|
||||
items = animdata_filter_gpencil(anim_data, data, filter_mode);
|
||||
items = animdata_filter_gpencil(ac, anim_data, data, filter_mode);
|
||||
break;
|
||||
}
|
||||
case ANIMCONT_MASK:
|
||||
|
@@ -929,7 +929,7 @@ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* mathods of offset */
|
||||
/* methods of offset */
|
||||
switch (offset_mode) {
|
||||
case KEYFRAME_PASTE_OFFSET_CFRA_START:
|
||||
offset = (float)(CFRA - animcopy_firstframe);
|
||||
|
@@ -40,6 +40,7 @@ set(INC_SYS
|
||||
set(SRC
|
||||
drawgpencil.c
|
||||
editaction_gpencil.c
|
||||
gpencil_brush.c
|
||||
gpencil_convert.c
|
||||
gpencil_data.c
|
||||
gpencil_edit.c
|
||||
|
@@ -1166,6 +1166,7 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i
|
||||
/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */
|
||||
void ED_gpencil_draw_2dimage(const bContext *C)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
@@ -1218,6 +1219,13 @@ void ED_gpencil_draw_2dimage(const bContext *C)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ED_screen_animation_playing(wm)) {
|
||||
/* don't show onionskins during animation playback/scrub (i.e. it obscures the poses)
|
||||
* OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes)
|
||||
*/
|
||||
dflag |= GP_DRAWDATA_NO_ONIONS;
|
||||
}
|
||||
|
||||
|
||||
/* draw it! */
|
||||
gp_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, sa->spacetype);
|
||||
@@ -1228,6 +1236,7 @@ void ED_gpencil_draw_2dimage(const bContext *C)
|
||||
* second time with onlyv2d=0 for screen-aligned strokes */
|
||||
void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
@@ -1246,6 +1255,8 @@ void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d)
|
||||
|
||||
/* draw it! */
|
||||
if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS);
|
||||
if (ED_screen_animation_playing(wm)) dflag |= GP_DRAWDATA_NO_ONIONS;
|
||||
|
||||
gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype);
|
||||
|
||||
/* draw status text (if in screen/pixel-space) */
|
||||
@@ -1257,7 +1268,7 @@ void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d)
|
||||
/* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly
|
||||
* Note: this gets called twice - first time with only3d=1 to draw 3d-strokes,
|
||||
* second time with only3d=0 for screen-aligned strokes */
|
||||
void ED_gpencil_draw_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
|
||||
void ED_gpencil_draw_view3d(wmWindowManager *wm, Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
|
||||
{
|
||||
bGPdata *gpd;
|
||||
int dflag = 0;
|
||||
@@ -1300,13 +1311,15 @@ void ED_gpencil_draw_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d)
|
||||
dflag |= GP_DRAWDATA_NOSTATUS;
|
||||
}
|
||||
|
||||
if ((wm == NULL) || ED_screen_animation_playing(wm)) {
|
||||
/* don't show onionskins during animation playback/scrub (i.e. it obscures the poses)
|
||||
* OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes)
|
||||
*/
|
||||
dflag |= GP_DRAWDATA_NO_ONIONS;
|
||||
}
|
||||
|
||||
/* draw it! */
|
||||
gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype);
|
||||
|
||||
/* draw status text (if in screen/pixel-space) */
|
||||
if (only3d == false) {
|
||||
gp_draw_status_text(gpd, ar);
|
||||
}
|
||||
}
|
||||
|
||||
void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype)
|
||||
|
@@ -44,11 +44,15 @@
|
||||
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "ED_anim_api.h"
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_keyframes_edit.h"
|
||||
#include "ED_markers.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
/* ***************************************** */
|
||||
/* NOTE ABOUT THIS FILE:
|
||||
* This file contains code for editing Grease Pencil data in the Action Editor
|
||||
@@ -268,7 +272,7 @@ void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // XXX disabled until grease pencil code stabilises again
|
||||
|
||||
/* -------------------------------------- */
|
||||
/* Copy and Paste Tools */
|
||||
/* - The copy/paste buffer currently stores a set of GP_Layers, with temporary
|
||||
@@ -280,118 +284,155 @@ void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type)
|
||||
*/
|
||||
|
||||
/* globals for copy/paste data (like for other copy/paste buffers) */
|
||||
ListBase gpcopybuf = {NULL, NULL};
|
||||
static int gpcopy_firstframe = 999999999;
|
||||
ListBase gp_anim_copybuf = {NULL, NULL};
|
||||
static int gp_anim_copy_firstframe = 999999999;
|
||||
static int gp_anim_copy_lastframe = -999999999;
|
||||
static int gp_anim_copy_cfra = 0;
|
||||
|
||||
|
||||
/* This function frees any MEM_calloc'ed copy/paste buffer data */
|
||||
void free_gpcopybuf()
|
||||
void ED_gpencil_anim_copybuf_free(void)
|
||||
{
|
||||
free_gpencil_layers(&gpcopybuf);
|
||||
free_gpencil_layers(&gp_anim_copybuf);
|
||||
BLI_listbase_clear(&gp_anim_copybuf);
|
||||
|
||||
BLI_listbase_clear(&gpcopybuf);
|
||||
gpcopy_firstframe = 999999999;
|
||||
gp_anim_copy_firstframe = 999999999;
|
||||
gp_anim_copy_lastframe = -999999999;
|
||||
gp_anim_copy_cfra = 0;
|
||||
}
|
||||
|
||||
|
||||
/* This function adds data to the copy/paste buffer, freeing existing data first
|
||||
* Only the selected GP-layers get their selected keyframes copied.
|
||||
*
|
||||
* Returns whether the copy operation was successful or not
|
||||
*/
|
||||
void copy_gpdata()
|
||||
bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
|
||||
{
|
||||
ListBase act_data = {NULL, NULL};
|
||||
bActListElem *ale;
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
void *data;
|
||||
short datatype;
|
||||
|
||||
Scene *scene = ac->scene;
|
||||
|
||||
|
||||
/* clear buffer first */
|
||||
free_gpcopybuf();
|
||||
|
||||
/* get data */
|
||||
data = get_action_context(&datatype);
|
||||
if (data == NULL) return;
|
||||
if (datatype != ACTCONT_GPENCIL) return;
|
||||
ED_gpencil_anim_copybuf_free();
|
||||
|
||||
/* filter data */
|
||||
filter = (ACTFILTER_VISIBLE | ACTFILTER_SEL);
|
||||
actdata_filter(&act_data, filter, data, datatype);
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
|
||||
|
||||
/* assume that each of these is an ipo-block */
|
||||
for (ale = act_data.first; ale; ale = ale->next) {
|
||||
bGPDlayer *gpls, *gpln;
|
||||
bGPDframe *gpf, *gpfn;
|
||||
|
||||
/* get new layer to put into buffer */
|
||||
gpls = (bGPDlayer *)ale->data;
|
||||
gpln = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
|
||||
|
||||
BLI_listbase_clear(&gpln->frames);
|
||||
BLI_strncpy(gpln->info, gpls->info, sizeof(gpln->info));
|
||||
|
||||
BLI_addtail(&gpcopybuf, gpln);
|
||||
/* assume that each of these is a GP layer */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
ListBase copied_frames = {NULL, NULL};
|
||||
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
||||
bGPDframe *gpf;
|
||||
|
||||
/* loop over frames, and copy only selected frames */
|
||||
for (gpf = gpls->frames.first; gpf; gpf = gpf->next) {
|
||||
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||
/* if frame is selected, make duplicate it and its strokes */
|
||||
if (gpf->flag & GP_FRAME_SELECT) {
|
||||
/* add frame to buffer */
|
||||
gpfn = gpencil_frame_duplicate(gpf);
|
||||
BLI_addtail(&gpln->frames, gpfn);
|
||||
/* make a copy of this frame */
|
||||
bGPDframe *new_frame = gpencil_frame_duplicate(gpf);
|
||||
BLI_addtail(&copied_frames, new_frame);
|
||||
|
||||
/* check if this is the earliest frame encountered so far */
|
||||
if (gpf->framenum < gpcopy_firstframe)
|
||||
gpcopy_firstframe = gpf->framenum;
|
||||
/* extend extents for keyframes encountered */
|
||||
if (gpf->framenum < gp_anim_copy_firstframe)
|
||||
gp_anim_copy_firstframe = gpf->framenum;
|
||||
if (gpf->framenum > gp_anim_copy_lastframe)
|
||||
gp_anim_copy_lastframe = gpf->framenum;
|
||||
}
|
||||
}
|
||||
|
||||
/* create a new layer in buffer if there were keyframes here */
|
||||
if (BLI_listbase_is_empty(&copied_frames) == false) {
|
||||
bGPDlayer *new_layer = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
|
||||
BLI_addtail(&gp_anim_copybuf, new_layer);
|
||||
|
||||
/* move over copied frames */
|
||||
BLI_movelisttolist(&new_layer->frames, &copied_frames);
|
||||
BLI_assert(copied_frames.first == NULL);
|
||||
|
||||
/* make a copy of the layer's name - for name-based matching later... */
|
||||
BLI_strncpy(new_layer->info, gpl->info, sizeof(new_layer->info));
|
||||
}
|
||||
}
|
||||
|
||||
/* check if anything ended up in the buffer */
|
||||
if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last))
|
||||
error("Nothing copied to buffer");
|
||||
/* in case 'relative' paste method is used */
|
||||
gp_anim_copy_cfra = CFRA;
|
||||
|
||||
/* free temp memory */
|
||||
BLI_freelistN(&act_data);
|
||||
/* clean up */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
|
||||
/* check if anything ended up in the buffer */
|
||||
if (ELEM(NULL, gp_anim_copybuf.first, gp_anim_copybuf.last)) {
|
||||
BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* report success */
|
||||
return true;
|
||||
}
|
||||
|
||||
void paste_gpdata(Scene *scene)
|
||||
|
||||
/* Pastes keyframes from buffer, and reports success */
|
||||
bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
|
||||
{
|
||||
ListBase act_data = {NULL, NULL};
|
||||
bActListElem *ale;
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
void *data;
|
||||
short datatype;
|
||||
|
||||
const int offset = (CFRA - gpcopy_firstframe);
|
||||
short no_name = 0;
|
||||
Scene *scene = ac->scene;
|
||||
bool no_name = false;
|
||||
int offset = 0;
|
||||
|
||||
/* check if buffer is empty */
|
||||
if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) {
|
||||
error("No data in buffer to paste");
|
||||
return;
|
||||
if (BLI_listbase_is_empty(&gp_anim_copybuf)) {
|
||||
BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste");
|
||||
return false;
|
||||
}
|
||||
/* check if single channel in buffer (disregard names if so) */
|
||||
if (gpcopybuf.first == gpcopybuf.last)
|
||||
no_name = 1;
|
||||
|
||||
/* get data */
|
||||
data = get_action_context(&datatype);
|
||||
if (data == NULL) return;
|
||||
if (datatype != ACTCONT_GPENCIL) return;
|
||||
/* check if single channel in buffer (disregard names if so) */
|
||||
if (gp_anim_copybuf.first == gp_anim_copybuf.last) {
|
||||
no_name = true;
|
||||
}
|
||||
|
||||
/* methods of offset (eKeyPasteOffset) */
|
||||
switch (offset_mode) {
|
||||
case KEYFRAME_PASTE_OFFSET_CFRA_START:
|
||||
offset = (CFRA - gp_anim_copy_firstframe);
|
||||
break;
|
||||
case KEYFRAME_PASTE_OFFSET_CFRA_END:
|
||||
offset = (CFRA - gp_anim_copy_lastframe);
|
||||
break;
|
||||
case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
|
||||
offset = (CFRA - gp_anim_copy_cfra);
|
||||
break;
|
||||
case KEYFRAME_PASTE_OFFSET_NONE:
|
||||
offset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* filter data */
|
||||
filter = (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT);
|
||||
actdata_filter(&act_data, filter, data, datatype);
|
||||
// TODO: try doing it with selection, then without selection imits
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
|
||||
|
||||
/* from selected channels */
|
||||
for (ale = act_data.first; ale; ale = ale->next) {
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
bGPDlayer *gpld = (bGPDlayer *)ale->data;
|
||||
bGPDlayer *gpls = NULL;
|
||||
bGPDframe *gpfs, *gpf;
|
||||
|
||||
|
||||
/* find suitable layer from buffer to use to paste from */
|
||||
for (gpls = gpcopybuf.first; gpls; gpls = gpls->next) {
|
||||
for (gpls = gp_anim_copybuf.first; gpls; gpls = gpls->next) {
|
||||
/* check if layer name matches */
|
||||
if ((no_name) || STREQ(gpls->info, gpld->info))
|
||||
if ((no_name) || STREQ(gpls->info, gpld->info)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* this situation might occur! */
|
||||
@@ -407,58 +448,21 @@ void paste_gpdata(Scene *scene)
|
||||
gpf = gpencil_layer_getframe(gpld, gpfs->framenum, 1);
|
||||
if (gpf) {
|
||||
bGPDstroke *gps, *gpsn;
|
||||
ScrArea *sa;
|
||||
|
||||
/* get area that gp-data comes from */
|
||||
//sa = gpencil_data_findowner((bGPdata *)ale->owner);
|
||||
sa = NULL;
|
||||
|
||||
/* this should be the right frame... as it may be a pre-existing frame,
|
||||
/* This should be the right frame... as it may be a pre-existing frame,
|
||||
* must make sure that only compatible stroke types get copied over
|
||||
* - we cannot just add a duplicate frame, as that would cause errors
|
||||
* - need to check for compatible types to minimize memory usage (copying 'junk' over)
|
||||
* - We cannot just add a duplicate frame, as that would cause errors
|
||||
* - For now, we don't check if the types will be compatible since we
|
||||
* don't have enough info to do so. Instead, we simply just paste,
|
||||
* af it works, it will show up.
|
||||
*/
|
||||
for (gps = gpfs->strokes.first; gps; gps = gps->next) {
|
||||
short stroke_ok;
|
||||
/* make a copy of stroke, then of its points array */
|
||||
gpsn = MEM_dupallocN(gps);
|
||||
gpsn->points = MEM_dupallocN(gps->points);
|
||||
|
||||
/* if there's an area, check that it supports this type of stroke */
|
||||
if (sa) {
|
||||
stroke_ok = 0;
|
||||
|
||||
/* check if spacetype supports this type of stroke
|
||||
* - NOTE: must sync this with gp_paint_initstroke() in gpencil.c
|
||||
*/
|
||||
switch (sa->spacetype) {
|
||||
case SPACE_VIEW3D: /* 3D-View: either screen-aligned or 3d-space */
|
||||
if ((gps->flag == 0) || (gps->flag & GP_STROKE_3DSPACE))
|
||||
stroke_ok = 1;
|
||||
break;
|
||||
|
||||
case SPACE_NODE: /* Nodes Editor: either screen-aligned or view-aligned */
|
||||
case SPACE_IMAGE: /* Image Editor: either screen-aligned or view\image-aligned */
|
||||
case SPACE_CLIP: /* Image Editor: either screen-aligned or view\image-aligned */
|
||||
if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DSPACE))
|
||||
stroke_ok = 1;
|
||||
break;
|
||||
|
||||
case SPACE_SEQ: /* Sequence Editor: either screen-aligned or view-aligned */
|
||||
if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DIMAGE))
|
||||
stroke_ok = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
stroke_ok = 1;
|
||||
|
||||
/* if stroke is ok, we make a copy of this stroke and add to frame */
|
||||
if (stroke_ok) {
|
||||
/* make a copy of stroke, then of its points array */
|
||||
gpsn = MEM_dupallocN(gps);
|
||||
gpsn->points = MEM_dupallocN(gps->points);
|
||||
|
||||
/* append stroke to frame */
|
||||
BLI_addtail(&gpf->strokes, gpsn);
|
||||
}
|
||||
/* append stroke to frame */
|
||||
BLI_addtail(&gpf->strokes, gpsn);
|
||||
}
|
||||
|
||||
/* if no strokes (i.e. new frame) added, free gpf */
|
||||
@@ -471,13 +475,10 @@ void paste_gpdata(Scene *scene)
|
||||
}
|
||||
}
|
||||
|
||||
/* free temp memory */
|
||||
BLI_freelistN(&act_data);
|
||||
|
||||
/* undo and redraw stuff */
|
||||
BIF_undo_push("Paste Grease Pencil Frames");
|
||||
/* clean up */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return true;
|
||||
}
|
||||
#endif /* XXX disabled until Grease Pencil code stabilises again... */
|
||||
|
||||
/* -------------------------------------- */
|
||||
/* Snap Tools */
|
||||
|
1691
source/blender/editors/gpencil/gpencil_brush.c
Normal file
1691
source/blender/editors/gpencil/gpencil_brush.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -77,6 +77,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "UI_resources.h"
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
@@ -106,9 +107,9 @@ enum {
|
||||
|
||||
/* RNA enum define */
|
||||
static EnumPropertyItem prop_gpencil_convertmodes[] = {
|
||||
{GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
|
||||
{GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
|
||||
{GP_STROKECONVERT_POLY, "POLY", 0, "Polygon Curve", ""},
|
||||
{GP_STROKECONVERT_PATH, "PATH", ICON_CURVE_PATH, "Path", "Animation path"},
|
||||
{GP_STROKECONVERT_CURVE, "CURVE", ICON_CURVE_BEZCURVE, "Bezier Curve", "Smooth Bezier curve"},
|
||||
{GP_STROKECONVERT_POLY, "POLY", ICON_MESH_DATA, "Polygon Curve", "Bezier curve with straight-line segments (vector handles)"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
@@ -59,12 +59,14 @@
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
|
||||
@@ -444,4 +446,220 @@ void GPENCIL_OT_reveal(wmOperatorType *ot)
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ***************** Lock/Unlock All Layers ************************ */
|
||||
|
||||
static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *gpl;
|
||||
|
||||
/* sanity checks */
|
||||
if (gpd == NULL)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* make all layers non-editable */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
gpl->flag |= GP_LAYER_LOCKED;
|
||||
}
|
||||
|
||||
/* notifiers */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_lock_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Lock All Layers";
|
||||
ot->idname = "GPENCIL_OT_lock_all";
|
||||
ot->description = "Lock all Grease Pencil layers to prevent them from being accidentally modified";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_lock_all_exec;
|
||||
ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* -------------------------- */
|
||||
|
||||
static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *gpl;
|
||||
|
||||
/* sanity checks */
|
||||
if (gpd == NULL)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* make all layers editable again */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
gpl->flag &= ~GP_LAYER_LOCKED;
|
||||
}
|
||||
|
||||
/* notifiers */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_unlock_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Unlock All Layers";
|
||||
ot->idname = "GPENCIL_OT_unlock_all";
|
||||
ot->description = "unlock all Grease Pencil layers so that they can be edited";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_unlock_all_exec;
|
||||
ot->poll = gp_reveal_poll; /* XXX: could use dedicated poll later */
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************** Isolate Layer **************************** */
|
||||
|
||||
static int gp_isolate_layer_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *layer = gpencil_layer_getactive(gpd);
|
||||
bGPDlayer *gpl;
|
||||
int flags = GP_LAYER_LOCKED;
|
||||
bool isolate = false;
|
||||
|
||||
if (RNA_boolean_get(op->ptr, "affect_visibility"))
|
||||
flags |= GP_LAYER_HIDE;
|
||||
|
||||
if (ELEM(NULL, gpd, layer)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active layer to isolate");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Test whether to isolate or clear all flags */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
/* Skip if this is the active layer */
|
||||
if (gpl == layer)
|
||||
continue;
|
||||
|
||||
/* If the flags aren't set, that means that the layer is
|
||||
* not alone, so we have some layers to isolate still
|
||||
*/
|
||||
if ((gpl->flag & flags) == 0) {
|
||||
isolate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set/Clear flags as appropriate */
|
||||
/* TODO: Include onionskinning on this list? */
|
||||
if (isolate) {
|
||||
/* Set flags on all "other" layers */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
if (gpl == layer)
|
||||
continue;
|
||||
else
|
||||
gpl->flag |= flags;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Clear flags - Restore everything else */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
gpl->flag &= ~flags;
|
||||
}
|
||||
}
|
||||
|
||||
/* notifiers */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Isolate Layer";
|
||||
ot->idname = "GPENCIL_OT_layer_isolate";
|
||||
ot->description = "Toggle whether the active layer is the only one that can be edited and/or visible";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_isolate_layer_exec;
|
||||
ot->poll = gp_active_layer_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility",
|
||||
"In addition to toggling the editability, also affect the visibility");
|
||||
}
|
||||
|
||||
/* ********************** Change Layer ***************************** */
|
||||
|
||||
static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
|
||||
{
|
||||
uiPopupMenu *pup;
|
||||
uiLayout *layout;
|
||||
|
||||
/* call the menu, which will call this operator again, hence the canceled */
|
||||
pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
|
||||
layout = UI_popup_menu_layout(pup);
|
||||
uiItemsEnumO(layout, "GPENCIL_OT_layer_change", "layer");
|
||||
UI_popup_menu_end(C, pup);
|
||||
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
static int gp_layer_change_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
bGPDlayer *gpl = NULL;
|
||||
int layer_num = RNA_enum_get(op->ptr, "layer");
|
||||
|
||||
/* Get layer or create new one */
|
||||
if (layer_num == -1) {
|
||||
/* Create layer */
|
||||
gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
|
||||
}
|
||||
else {
|
||||
/* Try to get layer */
|
||||
gpl = BLI_findlink(&gpd->layers, layer_num);
|
||||
|
||||
if (gpl == NULL) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set active layer */
|
||||
gpencil_layer_setactive(gpd, gpl);
|
||||
|
||||
/* updates */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_layer_change(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Change Layer";
|
||||
ot->idname = "GPENCIL_OT_layer_change";
|
||||
ot->description = "Change active Grease Pencil layer";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = gp_layer_change_invoke;
|
||||
ot->exec = gp_layer_change_exec;
|
||||
ot->poll = gp_active_layer_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* gp layer to use (dynamic enum) */
|
||||
ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
|
||||
RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
|
||||
}
|
||||
|
||||
/* ************************************************ */
|
||||
|
@@ -44,6 +44,7 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
@@ -58,20 +59,62 @@
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "gpencil_intern.h"
|
||||
|
||||
/* ************************************************ */
|
||||
/* Stroke Edit Mode Management */
|
||||
|
||||
static int gpencil_editmode_toggle_poll(bContext *C)
|
||||
{
|
||||
return ED_gpencil_data_get_active(C) != NULL;
|
||||
}
|
||||
|
||||
static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
|
||||
if (gpd == NULL)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* Just toggle editmode flag... */
|
||||
gpd->flag ^= GP_DATA_STROKE_EDITMODE;
|
||||
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Strokes Edit Mode Toggle";
|
||||
ot->idname = "GPENCIL_OT_editmode_toggle";
|
||||
ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gpencil_editmode_toggle_exec;
|
||||
ot->poll = gpencil_editmode_toggle_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
}
|
||||
|
||||
/* ************************************************ */
|
||||
/* Stroke Editing Operators */
|
||||
|
||||
@@ -233,7 +276,8 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot)
|
||||
*/
|
||||
|
||||
/* list of bGPDstroke instances */
|
||||
static ListBase gp_strokes_copypastebuf = {NULL, NULL};
|
||||
/* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
|
||||
ListBase gp_strokes_copypastebuf = {NULL, NULL};
|
||||
|
||||
/* Free copy/paste buffer data */
|
||||
void ED_gpencil_strokes_copybuf_free(void)
|
||||
@@ -339,7 +383,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
|
||||
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
else if (gp_strokes_copypastebuf.first == NULL) {
|
||||
else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
@@ -434,6 +478,110 @@ void GPENCIL_OT_paste(wmOperatorType *ot)
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ******************* Move To Layer ****************************** */
|
||||
|
||||
static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
|
||||
{
|
||||
uiPopupMenu *pup;
|
||||
uiLayout *layout;
|
||||
|
||||
/* call the menu, which will call this operator again, hence the canceled */
|
||||
pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
|
||||
layout = UI_popup_menu_layout(pup);
|
||||
uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
|
||||
UI_popup_menu_end(C, pup);
|
||||
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
// FIXME: allow moving partial strokes
|
||||
static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
bGPDlayer *target_layer = NULL;
|
||||
ListBase strokes = {NULL, NULL};
|
||||
int layer_num = RNA_enum_get(op->ptr, "layer");
|
||||
|
||||
/* Get layer or create new one */
|
||||
if (layer_num == -1) {
|
||||
/* Create layer */
|
||||
target_layer = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
|
||||
}
|
||||
else {
|
||||
/* Try to get layer */
|
||||
target_layer = BLI_findlink(&gpd->layers, layer_num);
|
||||
|
||||
if (target_layer == NULL) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract all strokes to move to this layer
|
||||
* NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
|
||||
* getting repeatedly moved
|
||||
*/
|
||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||
{
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps, *gpsn;
|
||||
|
||||
/* skip if no frame with strokes, or if this is the layer we're moving strokes to */
|
||||
if ((gpl == target_layer) || (gpf == NULL))
|
||||
continue;
|
||||
|
||||
/* make copies of selected strokes, and deselect these once we're done */
|
||||
for (gps = gpf->strokes.first; gps; gps = gpsn) {
|
||||
gpsn = gps->next;
|
||||
|
||||
/* skip strokes that are invalid for current view */
|
||||
if (ED_gpencil_stroke_can_use(C, gps) == false)
|
||||
continue;
|
||||
|
||||
/* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
BLI_remlink(&gpf->strokes, gps);
|
||||
BLI_addtail(&strokes, gps);
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* Paste them all in one go */
|
||||
if (strokes.first) {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
bGPDframe *gpf = gpencil_layer_getframe(target_layer, CFRA, true);
|
||||
|
||||
BLI_movelisttolist(&gpf->strokes, &strokes);
|
||||
BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
|
||||
}
|
||||
|
||||
/* updates */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Move Strokes to Layer";
|
||||
ot->idname = "GPENCIL_OT_move_to_layer";
|
||||
ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too?
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = gp_move_to_layer_invoke;
|
||||
ot->exec = gp_move_to_layer_exec;
|
||||
ot->poll = gp_stroke_edit_poll; // XXX?
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* gp layer to use (dynamic enum) */
|
||||
ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
|
||||
RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
|
||||
}
|
||||
|
||||
/* ******************* Delete Active Frame ************************ */
|
||||
|
||||
static int gp_actframe_delete_poll(bContext *C)
|
||||
@@ -497,6 +645,7 @@ typedef enum eGP_DeleteMode {
|
||||
GP_DELETEOP_FRAME = 2,
|
||||
} eGP_DeleteMode;
|
||||
|
||||
/* ----------------------------------- */
|
||||
|
||||
/* Delete selected strokes */
|
||||
static int gp_delete_selected_strokes(bContext *C)
|
||||
@@ -540,6 +689,8 @@ static int gp_delete_selected_strokes(bContext *C)
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------- */
|
||||
|
||||
/* Delete selected points but keep the stroke */
|
||||
static int gp_dissolve_selected_points(bContext *C)
|
||||
{
|
||||
@@ -621,6 +772,124 @@ static int gp_dissolve_selected_points(bContext *C)
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------- */
|
||||
|
||||
/* Temp data for storing information about an "island" of points
|
||||
* that should be kept when splitting up a stroke. Used in:
|
||||
* gp_stroke_delete_tagged_points()
|
||||
*/
|
||||
typedef struct tGPDeleteIsland {
|
||||
int start_idx;
|
||||
int end_idx;
|
||||
} tGPDeleteIsland;
|
||||
|
||||
|
||||
/* Split the given stroke into several new strokes, partitioning
|
||||
* it based on whether the stroke points have a particular flag
|
||||
* is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
|
||||
*
|
||||
* The algorithm used here is as follows:
|
||||
* 1) We firstly identify the number of "islands" of non-tagged points
|
||||
* which will all end up being in new strokes.
|
||||
* - In the most extreme case (i.e. every other vert is a 1-vert island),
|
||||
* we have at most n / 2 islands
|
||||
* - Once we start having larger islands than that, the number required
|
||||
* becomes much less
|
||||
* 2) Each island gets converted to a new stroke
|
||||
*/
|
||||
void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags)
|
||||
{
|
||||
tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
|
||||
bool in_island = false;
|
||||
int num_islands = 0;
|
||||
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* First Pass: Identify start/end of islands */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & tag_flags) {
|
||||
/* selected - stop accumulating to island */
|
||||
in_island = false;
|
||||
}
|
||||
else {
|
||||
/* unselected - start of a new island? */
|
||||
int idx;
|
||||
|
||||
if (in_island) {
|
||||
/* extend existing island */
|
||||
idx = num_islands - 1;
|
||||
islands[idx].end_idx = i;
|
||||
}
|
||||
else {
|
||||
/* start of new island */
|
||||
in_island = true;
|
||||
num_islands++;
|
||||
|
||||
idx = num_islands - 1;
|
||||
islands[idx].start_idx = islands[idx].end_idx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Watch out for special case where No islands = All points selected = Delete Stroke only */
|
||||
if (num_islands) {
|
||||
/* there are islands, so create a series of new strokes, adding them before the "next" stroke */
|
||||
int idx;
|
||||
|
||||
/* Create each new stroke... */
|
||||
for (idx = 0; idx < num_islands; idx++) {
|
||||
tGPDeleteIsland *island = &islands[idx];
|
||||
bGPDstroke *new_stroke = MEM_dupallocN(gps);
|
||||
|
||||
/* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
|
||||
new_stroke->totpoints = island->end_idx - island->start_idx + 1;
|
||||
new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
|
||||
|
||||
/* Copy over the relevant points */
|
||||
memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
|
||||
|
||||
|
||||
/* Each island corresponds to a new stroke. We must adjust the
|
||||
* timings of these new strokes:
|
||||
*
|
||||
* Each point's timing data is a delta from stroke's inittime, so as we erase some points from
|
||||
* the start of the stroke, we have to offset this inittime and all remaing points' delta values.
|
||||
* This way we get a new stroke with exactly the same timing as if user had started drawing from
|
||||
* the first non-removed point...
|
||||
*/
|
||||
{
|
||||
bGPDspoint *pts;
|
||||
float delta = gps->points[island->start_idx].time;
|
||||
int j;
|
||||
|
||||
new_stroke->inittime += (double)delta;
|
||||
|
||||
pts = new_stroke->points;
|
||||
for (j = 0; j < new_stroke->totpoints; j++, pts++) {
|
||||
pts->time -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new stroke to the frame */
|
||||
if (next_stroke) {
|
||||
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(&gpf->strokes, new_stroke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free islands */
|
||||
MEM_freeN(islands);
|
||||
|
||||
/* Delete the old stroke */
|
||||
MEM_freeN(gps->points);
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
}
|
||||
|
||||
|
||||
/* Split selected strokes into segments, splitting on selected points */
|
||||
static int gp_delete_selected_points(bContext *C)
|
||||
{
|
||||
@@ -644,89 +913,11 @@ static int gp_delete_selected_points(bContext *C)
|
||||
|
||||
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
/* deselect old stroke, since it will be used as template for the new strokes */
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
|
||||
/* The algorithm used here is as follows:
|
||||
* 1) We firstly identify the number of "islands" of non-selected points
|
||||
* which will all end up being in new strokes.
|
||||
* - In the most extreme case (i.e. every other vert is a 1-vert island),
|
||||
* we have at most n / 2 islands
|
||||
* - Once we start having larger islands than that, the number required
|
||||
* becomes much less
|
||||
* 2) Each island gets converted to a new stroke
|
||||
*/
|
||||
typedef struct tGPDeleteIsland {
|
||||
int start_idx;
|
||||
int end_idx;
|
||||
} tGPDeleteIsland;
|
||||
|
||||
tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
|
||||
bool in_island = false;
|
||||
int num_islands = 0;
|
||||
|
||||
/* First Pass: Identify start/end of islands */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
/* selected - stop accumulating to island */
|
||||
in_island = false;
|
||||
}
|
||||
else {
|
||||
/* unselected - start of a new island? */
|
||||
int idx;
|
||||
|
||||
if (in_island) {
|
||||
/* extend existing island */
|
||||
idx = num_islands - 1;
|
||||
islands[idx].end_idx = i;
|
||||
}
|
||||
else {
|
||||
/* start of new island */
|
||||
in_island = true;
|
||||
num_islands++;
|
||||
|
||||
idx = num_islands - 1;
|
||||
islands[idx].start_idx = islands[idx].end_idx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Watch out for special case where No islands = All points selected = Delete Stroke only */
|
||||
if (num_islands) {
|
||||
/* there are islands, so create a series of new strokes, adding them before the "next" stroke */
|
||||
int idx;
|
||||
|
||||
/* deselect old stroke, since it will be used as template for the new strokes */
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
|
||||
/* create each new stroke... */
|
||||
for (idx = 0; idx < num_islands; idx++) {
|
||||
tGPDeleteIsland *island = &islands[idx];
|
||||
bGPDstroke *new_stroke = MEM_dupallocN(gps);
|
||||
|
||||
/* compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
|
||||
new_stroke->totpoints = island->end_idx - island->start_idx + 1;
|
||||
new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
|
||||
|
||||
/* copy over the relevant points */
|
||||
memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
|
||||
|
||||
/* add new stroke to the frame */
|
||||
if (gpsn) {
|
||||
BLI_insertlinkbefore(&gpf->strokes, gpsn, new_stroke);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(&gpf->strokes, new_stroke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free islands */
|
||||
MEM_freeN(islands);
|
||||
|
||||
/* Delete the old stroke */
|
||||
MEM_freeN(gps->points);
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
/* delete unwanted points by splitting stroke into several smaller ones */
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
@@ -743,6 +934,7 @@ static int gp_delete_selected_points(bContext *C)
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------- */
|
||||
|
||||
static int gp_delete_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
@@ -812,4 +1004,190 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
}
|
||||
|
||||
/* ****************** Snapping - Strokes <-> Cursor ************************ */
|
||||
|
||||
/* Poll callback for snap operators */
|
||||
/* NOTE: For now, we only allow these in the 3D view, as other editors do not
|
||||
* define a cursor or gridstep which can be used
|
||||
*/
|
||||
static int gp_snap_poll(bContext *C)
|
||||
{
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
|
||||
return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
|
||||
}
|
||||
|
||||
/* --------------------------------- */
|
||||
|
||||
static int gp_snap_to_grid(bContext *C, wmOperator *op)
|
||||
{
|
||||
RegionView3D *rv3d = CTX_wm_region_data(C);
|
||||
float gridf = rv3d->gridview;
|
||||
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
// TOOD: if entire stroke is selected, offset entire stroke by same amount?
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
/* only if point is selected.. */
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
pt->x = gridf * floorf(0.5f + pt->x / gridf);
|
||||
pt->y = gridf * floorf(0.5f + pt->y / gridf);
|
||||
pt->z = gridf * floorf(0.5f + pt->z / gridf);
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Snap Selection to Grid";
|
||||
ot->idname = "GPENCIL_OT_snap_to_grid";
|
||||
ot->description = "Snap selected points to the nearest grid points";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_snap_to_grid;
|
||||
ot->poll = gp_snap_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ------------------------------- */
|
||||
|
||||
static int gp_snap_to_cursor(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
|
||||
const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
|
||||
const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d);
|
||||
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* only continue if this stroke is selected (editable doesn't guarantee this)... */
|
||||
if ((gps->flag & GP_STROKE_SELECT) == 0)
|
||||
continue;
|
||||
|
||||
if (use_offset) {
|
||||
float offset[3];
|
||||
|
||||
/* compute offset from first point of stroke to cursor */
|
||||
/* TODO: Allow using midpoint instead? */
|
||||
sub_v3_v3v3(offset, cursor_global, &gps->points->x);
|
||||
|
||||
/* apply offset to all points in the stroke */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
add_v3_v3(&pt->x, offset);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* affect each selected point */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
copy_v3_v3(&pt->x, cursor_global);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Snap Selection to Cursor";
|
||||
ot->idname = "GPENCIL_OT_snap_to_cursor";
|
||||
ot->description = "Snap selected points/strokes to the cursor";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_snap_to_cursor;
|
||||
ot->poll = gp_snap_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* props */
|
||||
ot->prop = RNA_def_boolean(ot->srna, "use_offset", true, "With Offset",
|
||||
"Offset the entire stroke instead of selected points only");
|
||||
}
|
||||
|
||||
/* ------------------------------- */
|
||||
|
||||
static int gp_snap_cursor_to_sel(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
|
||||
float *cursor = ED_view3d_cursor3d_get(scene, v3d);
|
||||
float centroid[3] = {0.0f};
|
||||
float min[3], max[3];
|
||||
size_t count = 0;
|
||||
|
||||
INIT_MINMAX(min, max);
|
||||
|
||||
/* calculate midpoints from selected points */
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* only continue if this stroke is selected (editable doesn't guarantee this)... */
|
||||
if ((gps->flag & GP_STROKE_SELECT) == 0)
|
||||
continue;
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
add_v3_v3(centroid, &pt->x);
|
||||
minmax_v3v3_v3(min, max, &pt->x);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
if (v3d->around == V3D_AROUND_CENTER_MEAN) {
|
||||
mul_v3_fl(centroid, 1.0f / (float)count);
|
||||
copy_v3_v3(cursor, centroid);
|
||||
}
|
||||
else {
|
||||
mid_v3_v3v3(cursor, min, max);
|
||||
}
|
||||
|
||||
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Snap Cursor to Selected Points";
|
||||
ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
|
||||
ot->description = "Snap cursor to center of selected points";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_snap_cursor_to_sel;
|
||||
ot->poll = gp_snap_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
|
||||
/* ************************************************ */
|
||||
|
@@ -44,6 +44,10 @@ struct ARegion;
|
||||
struct View2D;
|
||||
struct wmOperatorType;
|
||||
|
||||
struct PointerRNA;
|
||||
struct PropertyRNA;
|
||||
struct EnumPropertyItem;
|
||||
|
||||
|
||||
/* ***************************************************** */
|
||||
/* Internal API */
|
||||
@@ -96,12 +100,37 @@ void gp_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc);
|
||||
void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct bGPDspoint *pt,
|
||||
int *r_x, int *r_y);
|
||||
|
||||
/**
|
||||
* Convert a screenspace point to a 3D Grease Pencil coordinate.
|
||||
*
|
||||
* For use with editing tools where it is easier to perform the operations in 2D,
|
||||
* and then later convert the transformed points back to 3D.
|
||||
*
|
||||
* \param screeN_co The screenspace 2D coordinates to convert to
|
||||
* \param[out] r_out The resulting 3D coordinates of the input point
|
||||
*/
|
||||
bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float screen_co[2], float r_out[3]);
|
||||
|
||||
/* Poll Callbacks ------------------------------------ */
|
||||
/* gpencil_utils.c */
|
||||
|
||||
int gp_add_poll(struct bContext *C);
|
||||
int gp_active_layer_poll(struct bContext *C);
|
||||
|
||||
/* Copy/Paste Buffer --------------------------------- */
|
||||
/* gpencil_edit.c */
|
||||
|
||||
extern ListBase gp_strokes_copypastebuf;
|
||||
|
||||
/* Stroke Editing ------------------------------------ */
|
||||
|
||||
void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags);
|
||||
|
||||
/* Layers Enums -------------------------------------- */
|
||||
|
||||
struct EnumPropertyItem *ED_gpencil_layers_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
|
||||
struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
|
||||
|
||||
/* ***************************************************** */
|
||||
/* Operator Defines */
|
||||
|
||||
@@ -119,6 +148,8 @@ typedef enum eGPencil_PaintModes {
|
||||
|
||||
/* stroke editing ----- */
|
||||
|
||||
void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_select(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_select_all(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_select_circle(struct wmOperatorType *ot);
|
||||
@@ -135,6 +166,19 @@ void GPENCIL_OT_dissolve(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_copy(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_paste(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_move_to_layer(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_layer_change(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_snap_to_grid(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_snap_to_cursor(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_snap_cursor_to_center(struct wmOperatorType *ot);
|
||||
|
||||
|
||||
/* stroke sculpting -- */
|
||||
|
||||
void GPENCIL_OT_brush_paint(struct wmOperatorType *ot);
|
||||
|
||||
/* buttons editing --- */
|
||||
|
||||
void GPENCIL_OT_data_add(struct wmOperatorType *ot);
|
||||
@@ -148,6 +192,11 @@ void GPENCIL_OT_layer_duplicate(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_hide(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_reveal(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_lock_all(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_unlock_all(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_convert(struct wmOperatorType *ot);
|
||||
|
@@ -79,11 +79,25 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf)
|
||||
RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER);
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
|
||||
|
||||
/* Tablet Mappings for Drawing ------------------ */
|
||||
/* For now, only support direct drawing using the eraser, as most users using a tablet
|
||||
* may still want to use that as their primary pointing device!
|
||||
*/
|
||||
#if 0
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_STYLUS, KM_PRESS, 0, 0);
|
||||
RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW);
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
#endif
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0);
|
||||
RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER);
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
|
||||
/* Viewport Tools ------------------------------- */
|
||||
|
||||
/* Enter EditMode */
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", TABKEY, KM_PRESS, 0, DKEY);
|
||||
RNA_string_set(kmi->ptr, "data_path", "gpencil_data.use_stroke_edit_mode");
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_editmode_toggle", TABKEY, KM_PRESS, 0, DKEY);
|
||||
|
||||
/* Pie Menu - For standard tools */
|
||||
WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_tool_palette", QKEY, KM_PRESS, 0, DKEY);
|
||||
@@ -111,8 +125,10 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
/* Exit EditMode */
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", TABKEY, KM_PRESS, 0, 0);
|
||||
RNA_string_set(kmi->ptr, "data_path", "gpencil_data.use_stroke_edit_mode");
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_editmode_toggle", TABKEY, KM_PRESS, 0, 0);
|
||||
|
||||
/* Pie Menu - For settings/tools easy access */
|
||||
WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_sculpt", EKEY, KM_PRESS, 0, DKEY);
|
||||
|
||||
/* Brush Settings */
|
||||
/* NOTE: We cannot expose these in the standard keymap, as they will interfere with regular hotkeys
|
||||
@@ -185,6 +201,14 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_copy", CKEY, KM_PRESS, KM_OSKEY, 0);
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_paste", VKEY, KM_PRESS, KM_OSKEY, 0);
|
||||
#endif
|
||||
|
||||
/* snap */
|
||||
WM_keymap_add_menu(keymap, "GPENCIL_MT_snap", SKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
|
||||
|
||||
/* convert to geometry */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_convert", CKEY, KM_PRESS, KM_ALT, 0);
|
||||
|
||||
|
||||
/* Show/Hide */
|
||||
/* NOTE: These are available only in EditMode now, since they clash with general-purpose hotkeys */
|
||||
@@ -196,35 +220,62 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "unselected", true);
|
||||
|
||||
/* Isolate Layer */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0);
|
||||
|
||||
/* Move to Layer */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_move_to_layer", MKEY, KM_PRESS, 0, 0);
|
||||
|
||||
|
||||
|
||||
/* Brush-Based Editing:
|
||||
* EKEY + LMB = Single stroke, draw immediately
|
||||
* + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc.
|
||||
*
|
||||
* For the modal version, use D+E -> Sculpt
|
||||
*/
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, EKEY);
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, EKEY);
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
/*RNA_boolean_set(kmi->ptr, "use_invert", true);*/
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, EKEY);
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
/*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/
|
||||
|
||||
|
||||
/* Shift-FKEY = Sculpt Strength */
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength");
|
||||
|
||||
/* Ctrl-FKEY = Sculpt Brush Size */
|
||||
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
|
||||
RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size");
|
||||
|
||||
|
||||
|
||||
|
||||
/* Transform Tools */
|
||||
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", EVT_TWEAK_S, KM_ANY, 0, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_rotate", RKEY, KM_PRESS, 0, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_resize", SKEY, KM_PRESS, 0, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_mirror", MKEY, KM_PRESS, KM_CTRL, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_bend", WKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
WM_keymap_add_item(keymap, "TRANSFORM_OT_tosphere", SKEY, KM_PRESS, KM_ALT | KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
WM_keymap_add_item(keymap, "TRANSFORM_OT_shear", SKEY, KM_PRESS, KM_ALT | KM_CTRL | KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_transform", SKEY, KM_PRESS, KM_ALT, 0);
|
||||
RNA_enum_set(kmi->ptr, "mode", TFM_GPENCIL_SHRINKFATTEN);
|
||||
RNA_boolean_set(kmi->ptr, "gpencil_strokes", true);
|
||||
|
||||
/* Proportional Editing */
|
||||
ED_keymap_proportional_cycle(keyconf, keymap);
|
||||
@@ -249,6 +300,8 @@ void ED_operatortypes_gpencil(void)
|
||||
|
||||
/* Editing (Strokes) ------------ */
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_editmode_toggle);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_select);
|
||||
WM_operatortype_append(GPENCIL_OT_select_all);
|
||||
WM_operatortype_append(GPENCIL_OT_select_circle);
|
||||
@@ -265,6 +318,15 @@ void ED_operatortypes_gpencil(void)
|
||||
WM_operatortype_append(GPENCIL_OT_copy);
|
||||
WM_operatortype_append(GPENCIL_OT_paste);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_move_to_layer);
|
||||
WM_operatortype_append(GPENCIL_OT_layer_change);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_snap_to_grid);
|
||||
WM_operatortype_append(GPENCIL_OT_snap_to_cursor);
|
||||
WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_brush_paint);
|
||||
|
||||
/* Editing (Buttons) ------------ */
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_data_add);
|
||||
@@ -277,6 +339,9 @@ void ED_operatortypes_gpencil(void)
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_hide);
|
||||
WM_operatortype_append(GPENCIL_OT_reveal);
|
||||
WM_operatortype_append(GPENCIL_OT_lock_all);
|
||||
WM_operatortype_append(GPENCIL_OT_unlock_all);
|
||||
WM_operatortype_append(GPENCIL_OT_layer_isolate);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_active_frame_delete);
|
||||
|
||||
@@ -290,12 +355,14 @@ void ED_operatormacros_gpencil(void)
|
||||
wmOperatorType *ot;
|
||||
wmOperatorTypeMacro *otmacro;
|
||||
|
||||
/* Duplicate + Move = Interactively place newly duplicated strokes */
|
||||
ot = WM_operatortype_append_macro("GPENCIL_OT_duplicate_move", "Duplicate Strokes",
|
||||
"Make copies of the selected Grease Pencil strokes and move them",
|
||||
OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
WM_operatortype_macro_define(ot, "GPENCIL_OT_duplicate");
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
||||
RNA_boolean_set(otmacro->ptr, "gpencil_strokes", true);
|
||||
|
||||
}
|
||||
|
||||
/* ****************************************** */
|
||||
|
@@ -77,7 +77,33 @@
|
||||
/* ******************************************* */
|
||||
/* 'Globals' and Defines */
|
||||
|
||||
/* Temporary 'Stroke' Operation data */
|
||||
/* values for tGPsdata->status */
|
||||
typedef enum eGPencil_PaintStatus {
|
||||
GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */
|
||||
GP_STATUS_PAINTING, /* a stroke is in progress */
|
||||
GP_STATUS_ERROR, /* something wasn't correctly set up */
|
||||
GP_STATUS_DONE /* painting done */
|
||||
} eGPencil_PaintStatus;
|
||||
|
||||
/* Return flags for adding points to stroke buffer */
|
||||
typedef enum eGP_StrokeAdd_Result {
|
||||
GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */
|
||||
GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */
|
||||
GP_STROKEADD_NORMAL, /* point was successfully added */
|
||||
GP_STROKEADD_FULL /* cannot add any more points to buffer */
|
||||
} eGP_StrokeAdd_Result;
|
||||
|
||||
/* Runtime flags */
|
||||
typedef enum eGPencil_PaintFlags {
|
||||
GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */
|
||||
GP_PAINTFLAG_STROKEADDED = (1 << 1),
|
||||
GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2)
|
||||
} eGPencil_PaintFlags;
|
||||
|
||||
|
||||
/* Temporary 'Stroke' Operation data
|
||||
* "p" = op->customdata
|
||||
*/
|
||||
typedef struct tGPsdata {
|
||||
Scene *scene; /* current scene from context */
|
||||
|
||||
@@ -95,8 +121,13 @@ typedef struct tGPsdata {
|
||||
bGPDlayer *gpl; /* layer we're working on */
|
||||
bGPDframe *gpf; /* frame we're working on */
|
||||
|
||||
short status; /* current status of painting */
|
||||
short paintmode; /* mode for painting */
|
||||
char *align_flag; /* projection-mode flags (toolsettings - eGPencil_Placement_Flags) */
|
||||
|
||||
eGPencil_PaintStatus status; /* current status of painting */
|
||||
eGPencil_PaintModes paintmode; /* mode for painting */
|
||||
eGPencil_PaintFlags flags; /* flags that can get set during runtime (eGPencil_PaintFlags) */
|
||||
|
||||
short radius; /* radius of influence for eraser */
|
||||
|
||||
int mval[2]; /* current mouse-position */
|
||||
int mvalo[2]; /* previous recorded mouse-position */
|
||||
@@ -104,9 +135,6 @@ typedef struct tGPsdata {
|
||||
float pressure; /* current stylus pressure */
|
||||
float opressure; /* previous stylus pressure */
|
||||
|
||||
short radius; /* radius of influence for eraser */
|
||||
short flags; /* flags that can get set during runtime */
|
||||
|
||||
/* These need to be doubles, as (at least under unix) they are in seconds since epoch,
|
||||
* float (and its 7 digits precision) is definitively not enough here!
|
||||
* double, with its 15 digits precision, ensures us millisecond precision for a few centuries at least.
|
||||
@@ -124,29 +152,6 @@ typedef struct tGPsdata {
|
||||
void *erasercursor; /* radial cursor data for drawing eraser */
|
||||
} tGPsdata;
|
||||
|
||||
/* values for tGPsdata->status */
|
||||
enum {
|
||||
GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */
|
||||
GP_STATUS_PAINTING, /* a stroke is in progress */
|
||||
GP_STATUS_ERROR, /* something wasn't correctly set up */
|
||||
GP_STATUS_DONE /* painting done */
|
||||
};
|
||||
|
||||
/* Return flags for adding points to stroke buffer */
|
||||
enum {
|
||||
GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */
|
||||
GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */
|
||||
GP_STROKEADD_NORMAL, /* point was successfully added */
|
||||
GP_STROKEADD_FULL /* cannot add any more points to buffer */
|
||||
};
|
||||
|
||||
/* Runtime flags */
|
||||
enum {
|
||||
GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */
|
||||
GP_PAINTFLAG_STROKEADDED = (1 << 1),
|
||||
GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2)
|
||||
};
|
||||
|
||||
/* ------ */
|
||||
|
||||
/* maximum sizes of gp-session buffer */
|
||||
@@ -204,7 +209,7 @@ static int gpencil_draw_poll(bContext *C)
|
||||
static bool gpencil_project_check(tGPsdata *p)
|
||||
{
|
||||
bGPdata *gpd = p->gpd;
|
||||
return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (p->gpd->flag & (GP_DATA_DEPTH_VIEW | GP_DATA_DEPTH_STROKE)));
|
||||
return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)));
|
||||
}
|
||||
|
||||
/* ******************************************* */
|
||||
@@ -736,112 +741,6 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
|
||||
|
||||
/* --- 'Eraser' for 'Paint' Tool ------ */
|
||||
|
||||
/* eraser tool - remove segment from stroke/split stroke (after lasso inside) */
|
||||
static short gp_stroke_eraser_splitdel(bGPDframe *gpf, bGPDstroke *gps, int i)
|
||||
{
|
||||
bGPDspoint *pt_tmp = gps->points;
|
||||
bGPDstroke *gsn = NULL;
|
||||
|
||||
/* if stroke only had two points, get rid of stroke */
|
||||
if (gps->totpoints == 2) {
|
||||
/* free stroke points, then stroke */
|
||||
MEM_freeN(pt_tmp);
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
|
||||
/* nothing left in stroke, so stop */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if last segment, just remove segment from the stroke */
|
||||
else if (i == gps->totpoints - 2) {
|
||||
/* allocate new points array, and assign most of the old stroke there */
|
||||
gps->totpoints--;
|
||||
gps->points = MEM_mallocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
|
||||
memcpy(gps->points, pt_tmp, sizeof(bGPDspoint) * gps->totpoints);
|
||||
|
||||
/* free temp buffer */
|
||||
MEM_freeN(pt_tmp);
|
||||
|
||||
/* nothing left in stroke, so stop */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if first segment, just remove segment from the stroke */
|
||||
else if (i == 0) {
|
||||
/* allocate new points array, and assign most of the old stroke there */
|
||||
gps->totpoints--;
|
||||
gps->points = MEM_mallocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
|
||||
memcpy(gps->points, pt_tmp + 1, sizeof(bGPDspoint) * gps->totpoints);
|
||||
|
||||
/* We must adjust timings!
|
||||
* Each point's timing data is a delta from stroke's inittime, so as we erase the first
|
||||
* point of the stroke, we have to offset this inittime and all remaining points' delta values.
|
||||
* This way we get a new stroke with exactly the same timing as if user had started drawing from
|
||||
* the second point...
|
||||
*/
|
||||
{
|
||||
bGPDspoint *pts;
|
||||
float delta = pt_tmp[1].time;
|
||||
int j;
|
||||
|
||||
gps->inittime += (double)delta;
|
||||
|
||||
pts = gps->points;
|
||||
for (j = 0; j < gps->totpoints; j++, pts++) {
|
||||
pts->time -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
/* free temp buffer */
|
||||
MEM_freeN(pt_tmp);
|
||||
|
||||
/* no break here, as there might still be stuff to remove in this stroke */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* segment occurs in 'middle' of stroke, so split */
|
||||
else {
|
||||
/* duplicate stroke, and assign 'later' data to that stroke */
|
||||
gsn = MEM_dupallocN(gps);
|
||||
gsn->prev = gsn->next = NULL;
|
||||
BLI_insertlinkafter(&gpf->strokes, gps, gsn);
|
||||
|
||||
gsn->totpoints = gps->totpoints - i;
|
||||
gsn->points = MEM_mallocN(sizeof(bGPDspoint) * gsn->totpoints, "gp_stroke_points");
|
||||
memcpy(gsn->points, pt_tmp + i, sizeof(bGPDspoint) * gsn->totpoints);
|
||||
|
||||
/* We must adjust timings of this new stroke!
|
||||
* Each point's timing data is a delta from stroke's inittime, so as we erase the first
|
||||
* point of the stroke, we have to offset this inittime and all remaing points' delta values.
|
||||
* This way we get a new stroke with exactly the same timing as if user had started drawing from
|
||||
* the second point...
|
||||
*/
|
||||
{
|
||||
bGPDspoint *pts;
|
||||
float delta = pt_tmp[i].time;
|
||||
int j;
|
||||
|
||||
gsn->inittime += (double)delta;
|
||||
|
||||
pts = gsn->points;
|
||||
for (j = 0; j < gsn->totpoints; j++, pts++) {
|
||||
pts->time -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
/* adjust existing stroke */
|
||||
gps->totpoints = i;
|
||||
gps->points = MEM_mallocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
|
||||
memcpy(gps->points, pt_tmp, sizeof(bGPDspoint) * gps->totpoints);
|
||||
|
||||
/* free temp buffer */
|
||||
MEM_freeN(pt_tmp);
|
||||
|
||||
/* nothing left in stroke, so stop */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* which which point is infront (result should only be used for comparison) */
|
||||
static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
|
||||
{
|
||||
@@ -853,6 +752,7 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
|
||||
}
|
||||
}
|
||||
|
||||
/* only erase stroke points that are visible */
|
||||
static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y)
|
||||
{
|
||||
if ((p->sa->spacetype == SPACE_VIEW3D) &&
|
||||
@@ -874,15 +774,33 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons
|
||||
return false;
|
||||
}
|
||||
|
||||
/* apply a falloff effect to brush strength, based on distance */
|
||||
static float gp_stroke_eraser_calc_influence(tGPsdata *p, const int mval[2], const int radius, const int co[2])
|
||||
{
|
||||
/* Linear Falloff... */
|
||||
float distance = (float)len_v2v2_int(mval, co);
|
||||
float fac;
|
||||
|
||||
CLAMP(distance, 0.0f, (float)radius);
|
||||
fac = 1.0f - (distance / (float)radius);
|
||||
|
||||
/* Control this further using pen pressure */
|
||||
fac *= p->pressure;
|
||||
|
||||
/* Return influence factor computed here */
|
||||
return fac;
|
||||
}
|
||||
|
||||
/* eraser tool - evaluation per stroke */
|
||||
/* TODO: this could really do with some optimization (KD-Tree/BVH?) */
|
||||
static void gp_stroke_eraser_dostroke(tGPsdata *p,
|
||||
bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps,
|
||||
const int mval[2], const int mvalo[2],
|
||||
short rad, const rcti *rect, bGPDframe *gpf, bGPDstroke *gps)
|
||||
const int radius, const rcti *rect)
|
||||
{
|
||||
bGPDspoint *pt1, *pt2;
|
||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
int pc1[2] = {0};
|
||||
int pc2[2] = {0};
|
||||
int i;
|
||||
|
||||
if (gps->totpoints == 0) {
|
||||
@@ -892,56 +810,101 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
}
|
||||
else if (gps->totpoints == 1) {
|
||||
gp_point_to_xy(&p->gsc, gps, gps->points, &x0, &y0);
|
||||
gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
|
||||
|
||||
/* do boundbox check first */
|
||||
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
|
||||
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
|
||||
/* only check if point is inside */
|
||||
if (((x0 - mval[0]) * (x0 - mval[0]) + (y0 - mval[1]) * (y0 - mval[1])) <= rad * rad) {
|
||||
if (len_v2v2_int(mval, pc1) <= radius) {
|
||||
/* free stroke */
|
||||
// XXX: pressure sensitive eraser should apply here too?
|
||||
MEM_freeN(gps->points);
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* loop over the points in the stroke, checking for intersections
|
||||
* - an intersection will require the stroke to be split
|
||||
/* Pressure threshold at which stroke should be culled: Calculated as pressure value
|
||||
* below which we would have invisible strokes
|
||||
*/
|
||||
const float cull_thresh = (gpl->thickness) ? 1.0f / ((float)gpl->thickness) : 1.0f;
|
||||
|
||||
/* Amount to decrease the pressure of each point with each stroke */
|
||||
// TODO: Fetch from toolsettings, or compute based on thickness instead?
|
||||
const float strength = 0.1f;
|
||||
|
||||
/* Perform culling? */
|
||||
bool do_cull = false;
|
||||
|
||||
|
||||
/* Clear Tags
|
||||
*
|
||||
* Note: It's better this way, as we are sure that
|
||||
* we don't miss anything, though things will be
|
||||
* slightly slower as a result
|
||||
*/
|
||||
for (i = 0; i < gps->totpoints; i++) {
|
||||
bGPDspoint *pt = &gps->points[i];
|
||||
pt->flag &= ~GP_SPOINT_TAG;
|
||||
}
|
||||
|
||||
/* First Pass: Loop over the points in the stroke
|
||||
* 1) Thin out parts of the stroke under the brush
|
||||
* 2) Tag "too thin" parts for removal (in second pass)
|
||||
*/
|
||||
for (i = 0; (i + 1) < gps->totpoints; i++) {
|
||||
/* get points to work with */
|
||||
pt1 = gps->points + i;
|
||||
pt2 = gps->points + i + 1;
|
||||
|
||||
gp_point_to_xy(&p->gsc, gps, pt1, &x0, &y0);
|
||||
gp_point_to_xy(&p->gsc, gps, pt2, &x1, &y1);
|
||||
gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
|
||||
gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
|
||||
|
||||
/* check that point segment of the boundbox of the eraser stroke */
|
||||
if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) ||
|
||||
((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1)))
|
||||
/* Check that point segment of the boundbox of the eraser stroke */
|
||||
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
|
||||
((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1])))
|
||||
{
|
||||
/* check if point segment of stroke had anything to do with
|
||||
/* Check if point segment of stroke had anything to do with
|
||||
* eraser region (either within stroke painted, or on its lines)
|
||||
* - this assumes that linewidth is irrelevant
|
||||
*/
|
||||
if (gp_stroke_inside_circle(mval, mvalo, rad, x0, y0, x1, y1)) {
|
||||
if ((gp_stroke_eraser_is_occluded(p, pt1, x0, y0) == false) ||
|
||||
(gp_stroke_eraser_is_occluded(p, pt2, x1, y1) == false))
|
||||
if (gp_stroke_inside_circle(mval, mvalo, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
|
||||
if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) ||
|
||||
(gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false))
|
||||
{
|
||||
/* if function returns true, break this loop (as no more point to check) */
|
||||
if (gp_stroke_eraser_splitdel(gpf, gps, i))
|
||||
break;
|
||||
/* Point is affected: */
|
||||
/* 1) Adjust thickness
|
||||
* - Influence of eraser falls off with distance from the middle of the eraser
|
||||
* - Second point gets less influence, as it might get hit again in the next segment
|
||||
*/
|
||||
pt1->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc1) * strength;
|
||||
pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength / 2.0f;
|
||||
|
||||
/* 2) Tag any point with overly low influence for removal in the next pass */
|
||||
if (pt1->pressure < cull_thresh) {
|
||||
pt1->flag |= GP_SPOINT_TAG;
|
||||
do_cull = true;
|
||||
}
|
||||
if (pt2->pressure < cull_thresh) {
|
||||
pt2->flag |= GP_SPOINT_TAG;
|
||||
do_cull = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Second Pass: Remove any points that are tagged */
|
||||
if (do_cull) {
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* erase strokes which fall under the eraser strokes */
|
||||
static void gp_stroke_doeraser(tGPsdata *p)
|
||||
{
|
||||
bGPDframe *gpf = p->gpf;
|
||||
bGPDlayer *gpl;
|
||||
bGPDstroke *gps, *gpn;
|
||||
rcti rect;
|
||||
|
||||
@@ -960,10 +923,32 @@ static void gp_stroke_doeraser(tGPsdata *p)
|
||||
}
|
||||
}
|
||||
|
||||
/* loop over strokes, checking segments for intersections */
|
||||
for (gps = gpf->strokes.first; gps; gps = gpn) {
|
||||
gpn = gps->next;
|
||||
gp_stroke_eraser_dostroke(p, p->mval, p->mvalo, p->radius, &rect, gpf, gps);
|
||||
/* loop over all layers too, since while it's easy to restrict editing to
|
||||
* only a subset of layers, it is harder to perform the same erase operation
|
||||
* on multiple layers...
|
||||
*/
|
||||
for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
|
||||
/* only affect layer if it's editable (and visible) */
|
||||
if (gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) {
|
||||
continue;
|
||||
}
|
||||
else if (gpf == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* loop over strokes, checking segments for intersections */
|
||||
for (gps = gpf->strokes.first; gps; gps = gpn) {
|
||||
gpn = gps->next;
|
||||
|
||||
/* Not all strokes in the datablock may be valid in the current editor/context
|
||||
* (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
|
||||
*/
|
||||
if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) {
|
||||
gp_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, p->mvalo, p->radius, &rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1001,6 +986,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
bGPdata **gpd_ptr = NULL;
|
||||
ScrArea *curarea = CTX_wm_area(C);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
|
||||
/* make sure the active view (at the starting time) is a 3d-view */
|
||||
if (curarea == NULL) {
|
||||
@@ -1030,6 +1016,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
/* CAUTION: If this is the "toolbar", then this will change on the first stroke */
|
||||
p->sa = curarea;
|
||||
p->ar = ar;
|
||||
p->align_flag = &ts->gpencil_v3d_align;
|
||||
|
||||
if (ar->regiondata == NULL) {
|
||||
p->status = GP_STATUS_ERROR;
|
||||
@@ -1047,6 +1034,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
p->sa = curarea;
|
||||
p->ar = ar;
|
||||
p->v2d = &ar->v2d;
|
||||
p->align_flag = &ts->gpencil_v2d_align;
|
||||
break;
|
||||
}
|
||||
case SPACE_SEQ:
|
||||
@@ -1057,6 +1045,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
p->sa = curarea;
|
||||
p->ar = ar;
|
||||
p->v2d = &ar->v2d;
|
||||
p->align_flag = &ts->gpencil_seq_align;
|
||||
|
||||
/* check that gpencil data is allowed to be drawn */
|
||||
if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
|
||||
@@ -1075,6 +1064,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
p->sa = curarea;
|
||||
p->ar = ar;
|
||||
p->v2d = &ar->v2d;
|
||||
p->align_flag = &ts->gpencil_ima_align;
|
||||
break;
|
||||
}
|
||||
case SPACE_CLIP:
|
||||
@@ -1091,6 +1081,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
p->sa = curarea;
|
||||
p->ar = ar;
|
||||
p->v2d = &ar->v2d;
|
||||
p->align_flag = &ts->gpencil_v2d_align;
|
||||
|
||||
invert_m4_m4(p->imat, sc->unistabmat);
|
||||
|
||||
@@ -1167,6 +1158,12 @@ static tGPsdata *gp_session_initpaint(bContext *C)
|
||||
|
||||
gp_session_initdata(C, p);
|
||||
|
||||
/* radius for eraser circle is defined in userprefs now */
|
||||
/* NOTE: we do this here, so that if we exit immediately,
|
||||
* erase size won't get lost
|
||||
*/
|
||||
p->radius = U.gp_eraser;
|
||||
|
||||
/* return context data for running paint operator */
|
||||
return p;
|
||||
}
|
||||
@@ -1194,8 +1191,10 @@ static void gp_session_cleanup(tGPsdata *p)
|
||||
}
|
||||
|
||||
/* init new stroke */
|
||||
static void gp_paint_initstroke(tGPsdata *p, short paintmode)
|
||||
static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode)
|
||||
{
|
||||
Scene *scene = p->scene;
|
||||
|
||||
/* get active layer (or add a new one if non-existent) */
|
||||
p->gpl = gpencil_layer_getactive(p->gpd);
|
||||
if (p->gpl == NULL) {
|
||||
@@ -1212,15 +1211,61 @@ static void gp_paint_initstroke(tGPsdata *p, short paintmode)
|
||||
}
|
||||
|
||||
/* get active frame (add a new one if not matching frame) */
|
||||
p->gpf = gpencil_layer_getframe(p->gpl, p->scene->r.cfra, 1);
|
||||
if (p->gpf == NULL) {
|
||||
p->status = GP_STATUS_ERROR;
|
||||
if (G.debug & G_DEBUG)
|
||||
printf("Error: No frame created (gpencil_paint_init)\n");
|
||||
return;
|
||||
if (paintmode == GP_PAINTMODE_ERASER) {
|
||||
/* Eraser mode:
|
||||
* 1) Add new frames to all frames that we might touch,
|
||||
* 2) Ensure that p->gpf refers to the frame used for the active layer
|
||||
* (to avoid problems with other tools which expect it to exist)
|
||||
*/
|
||||
bGPDlayer *gpl;
|
||||
for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
/* Skip if layer not editable */
|
||||
if (gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED))
|
||||
continue;
|
||||
|
||||
/* Add a new frame if needed (and based off the active frame,
|
||||
* as we need some existing strokes to erase)
|
||||
*/
|
||||
gpl->actframe = gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY);
|
||||
|
||||
/* XXX: we omit GP_FRAME_PAINT here for now,
|
||||
* as it is only really useful for doing
|
||||
* paintbuffer drawing
|
||||
*/
|
||||
}
|
||||
|
||||
/* Ensure this gets set... */
|
||||
p->gpf = p->gpl->actframe;
|
||||
|
||||
if (p->gpf == NULL) {
|
||||
p->status = GP_STATUS_ERROR;
|
||||
//if (G.debug & G_DEBUG)
|
||||
printf("Error: No frame created (gpencil_paint_init)\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Drawing Modes - Add a new frame if needed on the active layer */
|
||||
ToolSettings *ts = p->scene->toolsettings;
|
||||
short add_frame_mode;
|
||||
|
||||
if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST)
|
||||
add_frame_mode = GP_GETFRAME_ADD_COPY;
|
||||
else
|
||||
add_frame_mode = GP_GETFRAME_ADD_NEW;
|
||||
|
||||
p->gpf = gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode);
|
||||
|
||||
if (p->gpf == NULL) {
|
||||
p->status = GP_STATUS_ERROR;
|
||||
if (G.debug & G_DEBUG)
|
||||
printf("Error: No frame created (gpencil_paint_init)\n");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
p->gpf->flag |= GP_FRAME_PAINT;
|
||||
}
|
||||
}
|
||||
else
|
||||
p->gpf->flag |= GP_FRAME_PAINT;
|
||||
|
||||
/* set 'eraser' for this stroke if using eraser */
|
||||
p->paintmode = paintmode;
|
||||
@@ -1251,7 +1296,7 @@ static void gp_paint_initstroke(tGPsdata *p, short paintmode)
|
||||
|
||||
/* when drawing in the camera view, in 2D space, set the subrect */
|
||||
p->subrect = NULL;
|
||||
if (!(p->gpd->flag & GP_DATA_VIEWALIGN)) {
|
||||
if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) {
|
||||
if (p->sa->spacetype == SPACE_VIEW3D) {
|
||||
View3D *v3d = p->sa->spacedata.first;
|
||||
RegionView3D *rv3d = p->ar->regiondata;
|
||||
@@ -1279,7 +1324,7 @@ static void gp_paint_initstroke(tGPsdata *p, short paintmode)
|
||||
|
||||
|
||||
/* check if points will need to be made in view-aligned space */
|
||||
if (p->gpd->flag & GP_DATA_VIEWALIGN) {
|
||||
if (*p->align_flag & GP_PROJECT_VIEWSPACE) {
|
||||
switch (p->sa->spacetype) {
|
||||
case SPACE_VIEW3D:
|
||||
{
|
||||
@@ -1308,7 +1353,7 @@ static void gp_paint_initstroke(tGPsdata *p, short paintmode)
|
||||
if (ELEM(NULL, sima, sima->image)) {
|
||||
/* make strokes be drawn in screen space */
|
||||
p->gpd->sbuffer_sflag &= ~GP_STROKE_2DSPACE;
|
||||
p->gpd->flag &= ~GP_DATA_VIEWALIGN;
|
||||
*(p->align_flag) &= ~GP_PROJECT_VIEWSPACE;
|
||||
}
|
||||
else {
|
||||
p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
|
||||
@@ -1417,6 +1462,17 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if tablet eraser is being used (when processing events) */
|
||||
static bool gpencil_is_tablet_eraser_active(const wmEvent *event)
|
||||
{
|
||||
if (event->tablet_data) {
|
||||
const wmTabletData *wmtab = event->tablet_data;
|
||||
return (wmtab->Active == EVT_TABLET_ERASER);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------- */
|
||||
|
||||
|
||||
@@ -1467,7 +1523,7 @@ static void gpencil_draw_cancel(bContext *C, wmOperator *op)
|
||||
static int gpencil_draw_init(bContext *C, wmOperator *op)
|
||||
{
|
||||
tGPsdata *p;
|
||||
int paintmode = RNA_enum_get(op->ptr, "mode");
|
||||
eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode");
|
||||
|
||||
/* check context */
|
||||
p = op->customdata = gp_session_initpaint(C);
|
||||
@@ -1484,9 +1540,6 @@ static int gpencil_draw_init(bContext *C, wmOperator *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* radius for eraser circle is defined in userprefs now */
|
||||
p->radius = U.gp_eraser;
|
||||
|
||||
/* everything is now setup ok */
|
||||
return 1;
|
||||
}
|
||||
@@ -1529,6 +1582,10 @@ static void gpencil_draw_status_indicators(tGPsdata *p)
|
||||
ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | "
|
||||
"ESC/Enter to end"));
|
||||
break;
|
||||
case GP_PAINTMODE_DRAW_POLY:
|
||||
ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | "
|
||||
"ESC/Enter to end"));
|
||||
break;
|
||||
|
||||
default: /* unhandled future cases */
|
||||
ED_area_headerprint(p->sa, IFACE_("Grease Pencil Session: ESC/Enter to end"));
|
||||
@@ -1622,9 +1679,6 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event)
|
||||
|
||||
tablet = (wmtab->Active != EVT_TABLET_NONE);
|
||||
p->pressure = wmtab->Pressure;
|
||||
|
||||
/* if (wmtab->Active == EVT_TABLET_ERASER) */
|
||||
/* TODO... this should get caught by the keymaps which call drawing in the first place */
|
||||
}
|
||||
else
|
||||
p->pressure = 1.0f;
|
||||
@@ -1819,7 +1873,6 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
|
||||
/* we may need to set up paint env again if we're resuming */
|
||||
/* XXX: watch it with the paintmode! in future,
|
||||
* it'd be nice to allow changing paint-mode when in sketching-sessions */
|
||||
/* XXX: with tablet events, we may event want to check for eraser here, for nicer tablet support */
|
||||
|
||||
if (gp_session_initdata(C, p))
|
||||
gp_paint_initstroke(p, p->paintmode);
|
||||
@@ -2008,14 +2061,14 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
/* Switch paintmode (temporarily if need be) based on which button was used
|
||||
* NOTE: This is to make it more convenient to erase strokes when using drawing sessions
|
||||
*/
|
||||
if (event->type == LEFTMOUSE) {
|
||||
/* restore drawmode to default */
|
||||
p->paintmode = RNA_enum_get(op->ptr, "mode");
|
||||
}
|
||||
else if (event->type == RIGHTMOUSE) {
|
||||
if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) {
|
||||
/* turn on eraser */
|
||||
p->paintmode = GP_PAINTMODE_ERASER;
|
||||
}
|
||||
else if (event->type == LEFTMOUSE) {
|
||||
/* restore drawmode to default */
|
||||
p->paintmode = RNA_enum_get(op->ptr, "mode");
|
||||
}
|
||||
|
||||
gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER);
|
||||
|
||||
|
@@ -67,10 +67,15 @@
|
||||
static int gpencil_select_poll(bContext *C)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
|
||||
|
||||
/* only if there's an active layer with an active frame */
|
||||
return (gpl && gpl->actframe);
|
||||
/* we just need some visible strokes, and to be in editmode */
|
||||
if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
|
||||
/* TODO: include a check for visible strokes? */
|
||||
if (gpd->layers.first)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
|
@@ -51,7 +51,9 @@
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "UI_resources.h"
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
@@ -76,8 +78,6 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr
|
||||
|
||||
switch (sa->spacetype) {
|
||||
case SPACE_VIEW3D: /* 3D-View */
|
||||
case SPACE_TIME: /* Timeline - XXX: this is a hack to get it to show GP keyframes for 3D view */
|
||||
case SPACE_ACTION: /* DepeSheet - XXX: this is a hack to get the keyframe jump operator to take GP Keyframes into account */
|
||||
{
|
||||
BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src,
|
||||
GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT));
|
||||
@@ -211,6 +211,45 @@ bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene, View3D *v3d)
|
||||
return gpd ? gpd : scene->gpd;
|
||||
}
|
||||
|
||||
/* ******************************************************** */
|
||||
/* Keyframe Indicator Checks */
|
||||
|
||||
/* Check whether there's an active GP keyframe on the current frame */
|
||||
bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra)
|
||||
{
|
||||
/* just check both for now... */
|
||||
// XXX: this could get confusing (e.g. if only on the object, but other places don't show this)
|
||||
if (scene->gpd) {
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(scene->gpd);
|
||||
if (gpl) {
|
||||
if (gpl->actframe) {
|
||||
// XXX: assumes that frame has been fetched already
|
||||
return (gpl->actframe->framenum == cfra);
|
||||
}
|
||||
else {
|
||||
/* XXX: disabled as could be too much of a penalty */
|
||||
/* return BKE_gpencil_layer_find_frame(gpl, cfra); */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ob && ob->gpd) {
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(ob->gpd);
|
||||
if (gpl) {
|
||||
if (gpl->actframe) {
|
||||
// XXX: assumes that frame has been fetched already
|
||||
return (gpl->actframe->framenum == cfra);
|
||||
}
|
||||
else {
|
||||
/* XXX: disabled as could be too much of a penalty */
|
||||
/* return BKE_gpencil_layer_find_frame(gpl, cfra); */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ******************************************************** */
|
||||
/* Poll Callbacks */
|
||||
|
||||
@@ -230,6 +269,92 @@ int gp_active_layer_poll(bContext *C)
|
||||
return (gpl != NULL);
|
||||
}
|
||||
|
||||
/* ******************************************************** */
|
||||
/* Dynamic Enums of GP Layers */
|
||||
/* NOTE: These include an option to create a new layer and use that... */
|
||||
|
||||
/* Just existing layers */
|
||||
EnumPropertyItem *ED_gpencil_layers_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
{
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
bGPDlayer *gpl;
|
||||
EnumPropertyItem *item = NULL, item_tmp = {0};
|
||||
int totitem = 0;
|
||||
int i = 0;
|
||||
|
||||
if (ELEM(NULL, C, gpd)) {
|
||||
return DummyRNA_DEFAULT_items;
|
||||
}
|
||||
|
||||
/* Existing layers */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next, i++) {
|
||||
item_tmp.identifier = gpl->info;
|
||||
item_tmp.name = gpl->info;
|
||||
item_tmp.value = i;
|
||||
|
||||
if (gpl->flag & GP_LAYER_ACTIVE)
|
||||
item_tmp.icon = ICON_GREASEPENCIL;
|
||||
else
|
||||
item_tmp.icon = ICON_NONE;
|
||||
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/* Existing + Option to add/use new layer */
|
||||
EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
{
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
bGPDlayer *gpl;
|
||||
EnumPropertyItem *item = NULL, item_tmp = {0};
|
||||
int totitem = 0;
|
||||
int i = 0;
|
||||
|
||||
if (ELEM(NULL, C, gpd)) {
|
||||
return DummyRNA_DEFAULT_items;
|
||||
}
|
||||
|
||||
/* Create new layer */
|
||||
/* TODO: have some way of specifying that we don't want this? */
|
||||
{
|
||||
/* active Keying Set */
|
||||
item_tmp.identifier = "__CREATE__";
|
||||
item_tmp.name = "New Layer";
|
||||
item_tmp.value = -1;
|
||||
item_tmp.icon = ICON_ZOOMIN;
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
|
||||
/* separator */
|
||||
RNA_enum_item_add_separator(&item, &totitem);
|
||||
}
|
||||
|
||||
/* Existing layers */
|
||||
for (gpl = gpd->layers.first, i = 0; gpl; gpl = gpl->next, i++) {
|
||||
item_tmp.identifier = gpl->info;
|
||||
item_tmp.name = gpl->info;
|
||||
item_tmp.value = i;
|
||||
|
||||
if (gpl->flag & GP_LAYER_ACTIVE)
|
||||
item_tmp.icon = ICON_GREASEPENCIL;
|
||||
else
|
||||
item_tmp.icon = ICON_NONE;
|
||||
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ******************************************************** */
|
||||
/* Brush Tool Core */
|
||||
|
||||
@@ -372,4 +497,37 @@ void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt,
|
||||
}
|
||||
}
|
||||
|
||||
/* Project screenspace coordinates to 3D-space
|
||||
* NOTE: We include this as a utility function, since the standard method
|
||||
* involves quite a few steps, which are invariably always the same
|
||||
* for all GPencil operations. So, it's nicer to just centralise these.
|
||||
* WARNING: Assumes that it is getting called in a 3D view only
|
||||
*/
|
||||
bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, Scene *scene, const float screen_co[2], float r_out[3])
|
||||
{
|
||||
View3D *v3d = gsc->sa->spacedata.first;
|
||||
RegionView3D *rv3d = gsc->ar->regiondata;
|
||||
float *rvec = ED_view3d_cursor3d_get(scene, v3d);
|
||||
float ref[3] = {rvec[0], rvec[1], rvec[2]};
|
||||
float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
|
||||
|
||||
float mval_f[2], mval_prj[2];
|
||||
float dvec[3];
|
||||
|
||||
copy_v2_v2(mval_f, screen_co);
|
||||
|
||||
if (ED_view3d_project_float_global(gsc->ar, ref, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
|
||||
sub_v2_v2v2(mval_f, mval_prj, mval_f);
|
||||
ED_view3d_win_to_delta(gsc->ar, mval_f, dvec, zfac);
|
||||
sub_v3_v3v3(r_out, rvec, dvec);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
zero_v3(r_out);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************** */
|
||||
|
@@ -41,7 +41,9 @@ struct bGPdata;
|
||||
struct bGPDlayer;
|
||||
struct bGPDframe;
|
||||
struct bGPDstroke;
|
||||
struct bAnimContext;
|
||||
struct PointerRNA;
|
||||
struct wmWindowManager;
|
||||
struct wmKeyConfig;
|
||||
|
||||
|
||||
@@ -77,6 +79,8 @@ struct bGPdata *ED_gpencil_data_get_active_direct(struct ID *screen_id, struct S
|
||||
/* 3D View */
|
||||
struct bGPdata *ED_gpencil_data_get_active_v3d(struct Scene *scene, struct View3D *v3d);
|
||||
|
||||
bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfra);
|
||||
|
||||
/* ----------- Stroke Editing Utilities ---------------- */
|
||||
|
||||
bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps);
|
||||
@@ -100,7 +104,7 @@ void ED_gpencil_strokes_copybuf_free(void);
|
||||
|
||||
void ED_gpencil_draw_2dimage(const struct bContext *C);
|
||||
void ED_gpencil_draw_view2d(const struct bContext *C, bool onlyv2d);
|
||||
void ED_gpencil_draw_view3d(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d);
|
||||
void ED_gpencil_draw_view3d(struct wmWindowManager *wm, struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d);
|
||||
void ED_gpencil_draw_ex(struct Scene *scene, struct bGPdata *gpd, int winx, int winy,
|
||||
const int cfra, const char spacetype);
|
||||
|
||||
@@ -122,11 +126,11 @@ void ED_gplayer_frames_keytype_set(struct bGPDlayer *gpl, short type);
|
||||
|
||||
void ED_gplayer_snap_frames(struct bGPDlayer *gpl, struct Scene *scene, short mode);
|
||||
|
||||
#if 0
|
||||
void free_gpcopybuf(void);
|
||||
void copy_gpdata(void);
|
||||
void paste_gpdata(void);
|
||||
void ED_gpencil_anim_copybuf_free(void);
|
||||
bool ED_gpencil_anim_copybuf_copy(struct bAnimContext *ac);
|
||||
bool ED_gpencil_anim_copybuf_paste(struct bAnimContext *ac, const short copy_mode);
|
||||
|
||||
#if 0
|
||||
void mirror_gplayer_frames(struct bGPDlayer *gpl, short mode);
|
||||
#endif
|
||||
|
||||
|
@@ -46,6 +46,7 @@
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
@@ -1509,6 +1510,7 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED(
|
||||
EnumPropertyItem *input = rna_enum_object_mode_items;
|
||||
EnumPropertyItem *item = NULL;
|
||||
Object *ob;
|
||||
bGPdata *gpd;
|
||||
int totitem = 0;
|
||||
|
||||
if (!C) /* needed for docs */
|
||||
@@ -1536,6 +1538,14 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED(
|
||||
/* We need at least this one! */
|
||||
RNA_enum_items_add_value(&item, &totitem, input, OB_MODE_OBJECT);
|
||||
}
|
||||
|
||||
/* On top of all the rest, GPencil Stroke Edit Mode
|
||||
* is available if there's a valid gp datablock...
|
||||
*/
|
||||
gpd = CTX_data_gpencil_data(C);
|
||||
if (gpd) {
|
||||
RNA_enum_items_add_value(&item, &totitem, rna_enum_object_mode_items, OB_MODE_GPENCIL);
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
|
||||
@@ -1560,6 +1570,8 @@ static const char *object_mode_op_string(int mode)
|
||||
return "PARTICLE_OT_particle_edit_toggle";
|
||||
if (mode == OB_MODE_POSE)
|
||||
return "OBJECT_OT_posemode_toggle";
|
||||
if (mode == OB_MODE_GPENCIL)
|
||||
return "GPENCIL_OT_editmode_toggle";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1571,6 +1583,8 @@ static bool object_mode_compat_test(Object *ob, ObjectMode mode)
|
||||
if (ob) {
|
||||
if (mode == OB_MODE_OBJECT)
|
||||
return true;
|
||||
else if (mode == OB_MODE_GPENCIL)
|
||||
return true; /* XXX: assume this is the case for now... */
|
||||
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
@@ -1625,13 +1639,45 @@ bool ED_object_mode_compat_set(bContext *C, Object *ob, int mode, ReportList *re
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int object_mode_set_poll(bContext *C)
|
||||
{
|
||||
/* Since Grease Pencil editmode is also handled here,
|
||||
* we have a special exception for allowing this operator
|
||||
* to still work in that case when there's no active object
|
||||
* so that users can exit editmode this way as per normal.
|
||||
*/
|
||||
if (ED_operator_object_active_editable(C))
|
||||
return true;
|
||||
else
|
||||
return (CTX_data_gpencil_data(C) != NULL);
|
||||
}
|
||||
|
||||
static int object_mode_set_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
ObjectMode mode = RNA_enum_get(op->ptr, "mode");
|
||||
ObjectMode restore_mode = (ob) ? ob->mode : OB_MODE_OBJECT;
|
||||
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
|
||||
|
||||
|
||||
if (gpd) {
|
||||
/* GP Mode is not bound to a specific object. Therefore,
|
||||
* we don't want it to be actually saved on any objects,
|
||||
* as weirdness can happen if you select other objects,
|
||||
* or load old files.
|
||||
*
|
||||
* Instead, we use the following 2 rules to ensure that
|
||||
* the mode selector works as expected:
|
||||
* 1) If there's no object, we want to enter editmode.
|
||||
* (i.e. with no object, we're in object mode)
|
||||
* 2) Otherwise, exit stroke editmode, so that we can
|
||||
* enter another mode...
|
||||
*/
|
||||
if (!ob || (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
|
||||
WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_EXEC_REGION_WIN, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ob || !object_mode_compat_test(ob, mode))
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
|
||||
@@ -1675,7 +1721,7 @@ void OBJECT_OT_mode_set(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = object_mode_set_exec;
|
||||
|
||||
ot->poll = ED_operator_object_active_editable;
|
||||
ot->poll = object_mode_set_poll; //ED_operator_object_active_editable;
|
||||
|
||||
/* flags */
|
||||
ot->flag = 0; /* no register/undo here, leave it to operators being called */
|
||||
|
@@ -2204,7 +2204,6 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
bDopeSheet ads = {NULL};
|
||||
DLRBT_Tree keys;
|
||||
ActKeyColumn *ak;
|
||||
@@ -2229,11 +2228,12 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
|
||||
|
||||
/* populate tree with keyframe nodes */
|
||||
scene_to_keylist(&ads, scene, &keys, NULL);
|
||||
gpencil_to_keylist(&ads, scene->gpd, &keys);
|
||||
|
||||
if (ob)
|
||||
if (ob) {
|
||||
ob_to_keylist(&ads, ob, &keys, NULL);
|
||||
|
||||
gpencil_to_keylist(&ads, gpd, &keys);
|
||||
gpencil_to_keylist(&ads, ob->gpd, &keys);
|
||||
}
|
||||
|
||||
{
|
||||
Mask *mask = CTX_data_edit_mask(C);
|
||||
|
@@ -549,9 +549,10 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op)
|
||||
|
||||
/* copy keyframes */
|
||||
if (ac.datatype == ANIMCONT_GPENCIL) {
|
||||
/* FIXME... */
|
||||
BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for grease pencil mode");
|
||||
return OPERATOR_CANCELLED;
|
||||
if (ED_gpencil_anim_copybuf_copy(&ac) == false) {
|
||||
/* Nothing got copied - An error about this should be been logged already */
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
else if (ac.datatype == ANIMCONT_MASK) {
|
||||
/* FIXME... */
|
||||
@@ -599,7 +600,13 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op)
|
||||
ac.reports = op->reports;
|
||||
|
||||
/* paste keyframes */
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
if (ac.datatype == ANIMCONT_GPENCIL) {
|
||||
if (ED_gpencil_anim_copybuf_paste(&ac, offset_mode) == false) {
|
||||
/* An error occurred - Reports should have been fired already */
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
else if (ac.datatype == ANIMCONT_MASK) {
|
||||
/* FIXME... */
|
||||
BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for grease pencil or mask mode");
|
||||
return OPERATOR_CANCELLED;
|
||||
|
@@ -349,14 +349,16 @@ static void time_draw_keyframes(const bContext *C, ARegion *ar)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
View2D *v2d = &ar->v2d;
|
||||
bool onlysel = ((scene->flag & SCE_KEYS_NO_SELONLY) == 0);
|
||||
|
||||
/* draw grease pencil keyframes (if available) */
|
||||
if (gpd) {
|
||||
UI_ThemeColor(TH_TIME_GP_KEYFRAME);
|
||||
time_draw_idblock_keyframes(v2d, (ID *)gpd, onlysel);
|
||||
/* draw grease pencil keyframes (if available) */
|
||||
UI_ThemeColor(TH_TIME_GP_KEYFRAME);
|
||||
if (scene->gpd) {
|
||||
time_draw_idblock_keyframes(v2d, (ID *)scene->gpd, onlysel);
|
||||
}
|
||||
if (ob && ob->gpd) {
|
||||
time_draw_idblock_keyframes(v2d, (ID *)ob->gpd, onlysel);
|
||||
}
|
||||
|
||||
/* draw scene keyframes first
|
||||
|
@@ -941,6 +941,8 @@ static void draw_selected_name(Scene *scene, Object *ob, rcti *rect)
|
||||
/* color depends on whether there is a keyframe */
|
||||
if (id_frame_has_keyframe((ID *)ob, /* BKE_scene_frame_get(scene) */ (float)cfra, ANIMFILTER_KEYS_LOCAL))
|
||||
UI_ThemeColor(TH_VERTEX_SELECT);
|
||||
else if (ED_gpencil_has_keyframe_v3d(scene, ob, cfra))
|
||||
UI_ThemeColor(TH_CFRAME); // XXX
|
||||
else
|
||||
UI_ThemeColor(TH_TEXT_HI);
|
||||
}
|
||||
@@ -2349,7 +2351,7 @@ void ED_view3d_draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
|
||||
ED_gpencil_draw_view3d(scene, v3d, ar, true);
|
||||
ED_gpencil_draw_view3d(NULL, scene, v3d, ar, true);
|
||||
}
|
||||
|
||||
v3d->zbuf = zbuf;
|
||||
@@ -2853,9 +2855,11 @@ static void view3d_draw_objects(
|
||||
|
||||
/* must be before xray draw which clears the depth buffer */
|
||||
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
|
||||
wmWindowManager *wm = (C != NULL) ? CTX_wm_manager(C) : NULL;
|
||||
|
||||
/* must be before xray draw which clears the depth buffer */
|
||||
if (v3d->zbuf) glDisable(GL_DEPTH_TEST);
|
||||
ED_gpencil_draw_view3d(scene, v3d, ar, true);
|
||||
ED_gpencil_draw_view3d(wm, scene, v3d, ar, true);
|
||||
if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
@@ -3266,7 +3270,7 @@ void ED_view3d_draw_offscreen(
|
||||
|
||||
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
|
||||
/* draw grease-pencil stuff - needed to get paint-buffer shown too (since it's 2D) */
|
||||
ED_gpencil_draw_view3d(scene, v3d, ar, false);
|
||||
ED_gpencil_draw_view3d(NULL, scene, v3d, ar, false);
|
||||
}
|
||||
|
||||
/* freeing the images again here could be done after the operator runs, leaving for now */
|
||||
@@ -3481,6 +3485,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(
|
||||
|
||||
if (use_solid_tex)
|
||||
v3d.flag2 |= V3D_SOLID_TEX;
|
||||
|
||||
if (draw_background)
|
||||
v3d.flag3 |= V3D_SHOW_WORLD;
|
||||
|
||||
rv3d.persp = RV3D_CAMOB;
|
||||
|
||||
@@ -3949,6 +3956,7 @@ static void view3d_main_region_draw_info(const bContext *C, Scene *scene,
|
||||
ARegion *ar, View3D *v3d,
|
||||
const char *grid_unit, bool render_border)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
RegionView3D *rv3d = ar->regiondata;
|
||||
rcti rect;
|
||||
|
||||
@@ -3972,7 +3980,7 @@ static void view3d_main_region_draw_info(const bContext *C, Scene *scene,
|
||||
|
||||
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
|
||||
/* draw grease-pencil stuff - needed to get paint-buffer shown too (since it's 2D) */
|
||||
ED_gpencil_draw_view3d(scene, v3d, ar, false);
|
||||
ED_gpencil_draw_view3d(wm, scene, v3d, ar, false);
|
||||
}
|
||||
|
||||
if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) {
|
||||
@@ -3999,8 +4007,6 @@ static void view3d_main_region_draw_info(const bContext *C, Scene *scene,
|
||||
}
|
||||
|
||||
if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) {
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
|
||||
if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) {
|
||||
ED_scene_draw_fps(scene, &rect);
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
@@ -287,6 +288,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
|
||||
PointerRNA v3dptr, toolsptr, sceneptr;
|
||||
Object *ob = OBACT;
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
uiBlock *block;
|
||||
uiLayout *row;
|
||||
bool is_paint = false;
|
||||
@@ -303,7 +305,10 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
|
||||
UI_block_emboss_set(block, UI_EMBOSS);
|
||||
|
||||
/* mode */
|
||||
if (ob) {
|
||||
if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
|
||||
modeselect = OB_MODE_GPENCIL;
|
||||
}
|
||||
else if (ob) {
|
||||
modeselect = ob->mode;
|
||||
is_paint = ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT);
|
||||
}
|
||||
|
@@ -7767,45 +7767,7 @@ static void createTransGPencil(bContext *C, TransInfo *t)
|
||||
*/
|
||||
// XXX: should this be allowed when framelock is enabled?
|
||||
if (gpf->framenum != cfra) {
|
||||
bGPDframe *new_frame = gpencil_frame_duplicate(gpf);
|
||||
bGPDframe *gf;
|
||||
bool found = false;
|
||||
|
||||
/* Find frame to insert it before */
|
||||
for (gf = gpf->next; gf; gf = gf->next) {
|
||||
if (gf->framenum > cfra) {
|
||||
/* Add it here */
|
||||
BLI_insertlinkbefore(&gpl->frames, gf, new_frame);
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else if (gf->framenum == cfra) {
|
||||
/* This only happens when we're editing with framelock on...
|
||||
* - Delete the new frame and don't do anything else here...
|
||||
*/
|
||||
//printf("GP Frame convert to TransData - Copy aborted for frame %d -> %d\n", gpf->framenum, gf->framenum);
|
||||
free_gpencil_strokes(new_frame);
|
||||
MEM_freeN(new_frame);
|
||||
new_frame = NULL;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == false) {
|
||||
/* Add new frame to the end */
|
||||
BLI_addtail(&gpl->frames, new_frame);
|
||||
}
|
||||
|
||||
/* Edit the new frame instead, if it did get created + added */
|
||||
if (new_frame) {
|
||||
// TODO: tag this one as being "newly created" so that we can remove it if the edit is cancelled
|
||||
new_frame->framenum = cfra;
|
||||
|
||||
gpf = new_frame;
|
||||
}
|
||||
gpf = gpencil_frame_addcopy(gpl, cfra);
|
||||
}
|
||||
|
||||
/* Loop over strokes, adding TransData for points as needed... */
|
||||
@@ -7930,7 +7892,8 @@ void createTransData(bContext *C, TransInfo *t)
|
||||
}
|
||||
}
|
||||
else if (t->options & CTX_GPENCIL_STROKES) {
|
||||
t->flag |= T_POINTS; // XXX...
|
||||
t->options |= CTX_GPENCIL_STROKES;
|
||||
t->flag |= T_POINTS;
|
||||
createTransGPencil(C, t);
|
||||
|
||||
if (t->data && (t->flag & T_PROP_EDIT)) {
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
@@ -1095,6 +1096,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
PropertyRNA *prop;
|
||||
|
||||
t->scene = sce;
|
||||
@@ -1164,6 +1166,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
|
||||
t->remove_on_cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* GPencil editing context */
|
||||
if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
|
||||
t->options |= CTX_GPENCIL_STROKES;
|
||||
}
|
||||
|
||||
/* Assign the space type, some exceptions for running in different mode */
|
||||
if (sa == NULL) {
|
||||
|
@@ -37,12 +37,14 @@
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
@@ -272,6 +274,8 @@ static int calc_manipulator_stats(const bContext *C)
|
||||
RegionView3D *rv3d = ar->regiondata;
|
||||
Base *base;
|
||||
Object *ob = OBACT;
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
|
||||
int a, totsel = 0;
|
||||
|
||||
/* transform widget matrix */
|
||||
@@ -282,8 +286,32 @@ static int calc_manipulator_stats(const bContext *C)
|
||||
/* transform widget centroid/center */
|
||||
INIT_MINMAX(scene->twmin, scene->twmax);
|
||||
zero_v3(scene->twcent);
|
||||
|
||||
if (obedit) {
|
||||
|
||||
if (is_gp_edit) {
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
/* we're only interested in selected points here... */
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* Change selection status of all points, then make the stroke match */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
calc_tw_center(scene, &pt->x);
|
||||
totsel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* selection center */
|
||||
if (totsel) {
|
||||
mul_v3_fl(scene->twcent, 1.0f / (float)totsel); /* centroid! */
|
||||
}
|
||||
}
|
||||
else if (obedit) {
|
||||
ob = obedit;
|
||||
if ((ob->lay & v3d->lay) == 0) return 0;
|
||||
|
||||
@@ -546,7 +574,7 @@ static int calc_manipulator_stats(const bContext *C)
|
||||
}
|
||||
|
||||
/* global, local or normal orientation? */
|
||||
if (ob && totsel) {
|
||||
if (ob && totsel && !is_gp_edit) {
|
||||
|
||||
switch (v3d->twmode) {
|
||||
|
||||
@@ -1595,9 +1623,12 @@ void BIF_draw_manipulator(const bContext *C)
|
||||
case V3D_AROUND_CENTER_BOUNDS:
|
||||
case V3D_AROUND_ACTIVE:
|
||||
{
|
||||
Object *ob;
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
Object *ob = OBACT;
|
||||
|
||||
if (((v3d->around == V3D_AROUND_ACTIVE) && (scene->obedit == NULL)) &&
|
||||
((ob = OBACT) && !(ob->mode & OB_MODE_POSE)))
|
||||
((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
|
||||
(!(ob->mode & OB_MODE_POSE)))
|
||||
{
|
||||
copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
|
||||
}
|
||||
|
@@ -596,6 +596,9 @@ typedef enum eDopeSheet_FilterFlag {
|
||||
ADS_FILTER_BY_FCU_NAME = (1 << 27), /* for F-Curves, filter by the displayed name (i.e. to isolate all Location curves only) */
|
||||
ADS_FILTER_ONLY_ERRORS = (1 << 28), /* show only F-Curves which are disabled/have errors - for debugging drivers */
|
||||
|
||||
/* GPencil Mode */
|
||||
ADS_FILTER_GP_3DONLY = (1 << 29), /* GP Mode - Only show datablocks used in the scene */
|
||||
|
||||
/* combination filters (some only used at runtime) */
|
||||
ADS_FILTER_NOOBDATA = (ADS_FILTER_NOCAM | ADS_FILTER_NOMAT | ADS_FILTER_NOLAM | ADS_FILTER_NOCUR | ADS_FILTER_NOPART | ADS_FILTER_NOARM | ADS_FILTER_NOSPK | ADS_FILTER_NOMODIFIERS)
|
||||
} eDopeSheet_FilterFlag;
|
||||
|
@@ -51,7 +51,10 @@ typedef struct bGPDspoint {
|
||||
/* bGPDspoint->flag */
|
||||
typedef enum eGPDspoint_Flag {
|
||||
/* stroke point is selected (for editing) */
|
||||
GP_SPOINT_SELECT = (1 << 0)
|
||||
GP_SPOINT_SELECT = (1 << 0),
|
||||
|
||||
/* stroke point is tagged (for some editing operation) */
|
||||
GP_SPOINT_TAG = (1 << 1),
|
||||
} eGPSPoint_Flag;
|
||||
|
||||
/* Grease-Pencil Annotations - 'Stroke'
|
||||
@@ -190,6 +193,7 @@ typedef enum eGPdata_Flag {
|
||||
/* is the block overriding all clicks? */
|
||||
/* GP_DATA_EDITPAINT = (1 << 3), */
|
||||
|
||||
/* ------------------------------------------------ DEPRECATED */
|
||||
/* new strokes are added in viewport space */
|
||||
GP_DATA_VIEWALIGN = (1 << 4),
|
||||
|
||||
@@ -198,9 +202,13 @@ typedef enum eGPdata_Flag {
|
||||
GP_DATA_DEPTH_STROKE = (1 << 6),
|
||||
|
||||
GP_DATA_DEPTH_STROKE_ENDPOINTS = (1 << 7),
|
||||
/* ------------------------------------------------ DEPRECATED */
|
||||
|
||||
/* Stroke Editing Mode - Toggle to enable alternative keymap for easier editing of stroke points */
|
||||
GP_DATA_STROKE_EDITMODE = (1 << 8)
|
||||
GP_DATA_STROKE_EDITMODE = (1 << 8),
|
||||
|
||||
/* Convenience/cache flag to make it easier to quickly toggle onion skinning on/off */
|
||||
GP_DATA_SHOW_ONIONSKINS = (1 << 9)
|
||||
} eGPdata_Flag;
|
||||
|
||||
#endif /* __DNA_GPENCIL_TYPES_H__ */
|
||||
|
@@ -675,6 +675,7 @@ typedef enum ObjectMode {
|
||||
OB_MODE_TEXTURE_PAINT = 1 << 4,
|
||||
OB_MODE_PARTICLE_EDIT = 1 << 5,
|
||||
OB_MODE_POSE = 1 << 6,
|
||||
OB_MODE_GPENCIL = 1 << 7, /* NOTE: Just a dummy to make the UI nicer */
|
||||
} ObjectMode;
|
||||
|
||||
/* any mode where the brush system is used */
|
||||
|
@@ -911,6 +911,7 @@ typedef enum StereoViews {
|
||||
STEREO_MONO_ID = 3,
|
||||
} StereoViews;
|
||||
|
||||
/* *************************************************************** */
|
||||
/* Markers */
|
||||
|
||||
typedef struct TimeMarker {
|
||||
@@ -1041,6 +1042,7 @@ typedef struct Sculpt {
|
||||
typedef struct UvSculpt {
|
||||
Paint paint;
|
||||
} UvSculpt;
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/* Vertex Paint */
|
||||
|
||||
@@ -1066,6 +1068,65 @@ enum {
|
||||
VP_ONLYVGROUP = (1 << 7) /* weight paint only */
|
||||
};
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/* GPencil Stroke Sculpting */
|
||||
|
||||
/* Brush types */
|
||||
typedef enum eGP_EditBrush_Types {
|
||||
GP_EDITBRUSH_TYPE_SMOOTH = 0,
|
||||
GP_EDITBRUSH_TYPE_THICKNESS = 1,
|
||||
GP_EDITBRUSH_TYPE_GRAB = 2,
|
||||
GP_EDITBRUSH_TYPE_PUSH = 3,
|
||||
GP_EDITBRUSH_TYPE_TWIST = 4,
|
||||
GP_EDITBRUSH_TYPE_PINCH = 5,
|
||||
GP_EDITBRUSH_TYPE_RANDOMISE = 6,
|
||||
GP_EDITBRUSH_TYPE_SUBDIVIDE = 7,
|
||||
GP_EDITBRUSH_TYPE_SIMPLIFY = 8,
|
||||
GP_EDITBRUSH_TYPE_CLONE = 9,
|
||||
|
||||
/* !!! Update GP_EditBrush_Data brush[###]; below !!! */
|
||||
TOT_GP_EDITBRUSH_TYPES
|
||||
} eGP_EditBrush_Types;
|
||||
|
||||
|
||||
/* Settings for a GPencil Stroke Sculpting Brush */
|
||||
typedef struct GP_EditBrush_Data {
|
||||
short size; /* radius of brush */
|
||||
short flag; /* eGP_EditBrush_Flag */
|
||||
float strength; /* strength of effect */
|
||||
} GP_EditBrush_Data;
|
||||
|
||||
/* GP_EditBrush_Data.flag */
|
||||
typedef enum eGP_EditBrush_Flag {
|
||||
/* invert the effect of the brush */
|
||||
GP_EDITBRUSH_FLAG_INVERT = (1 << 0),
|
||||
/* adjust strength using pen pressure */
|
||||
GP_EDITBRUSH_FLAG_USE_PRESSURE = (1 << 1),
|
||||
|
||||
/* strength of brush falls off with distance from cursor */
|
||||
GP_EDITBRUSH_FLAG_USE_FALLOFF = (1 << 2),
|
||||
|
||||
/* smooth brush affects pressure values as well */
|
||||
GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE = (1 << 3)
|
||||
} eGP_EditBrush_Flag;
|
||||
|
||||
|
||||
|
||||
/* GPencil Stroke Sculpting Settings */
|
||||
typedef struct GP_BrushEdit_Settings {
|
||||
GP_EditBrush_Data brush[10]; /* TOT_GP_EDITBRUSH_TYPES */
|
||||
void *paintcursor; /* runtime */
|
||||
|
||||
int brushtype; /* eGP_EditBrush_Types */
|
||||
int flag; /* eGP_BrushEdit_SettingsFlag */
|
||||
} GP_BrushEdit_Settings;
|
||||
|
||||
/* GP_BrushEdit_Settings.flag */
|
||||
typedef enum eGP_BrushEdit_SettingsFlag {
|
||||
/* only affect selected points */
|
||||
GP_BRUSHEDIT_FLAG_SELECT_MASK = (1 << 0)
|
||||
} eGP_BrushEdit_SettingsFlag;
|
||||
|
||||
/* *************************************************************** */
|
||||
/* Transform Orientations */
|
||||
|
||||
@@ -1172,6 +1233,10 @@ typedef enum {
|
||||
UNIFIED_PAINT_BRUSH_ALPHA_PRESSURE = (1 << 4)
|
||||
} UnifiedPaintSettingsFlags;
|
||||
|
||||
/* *************************************************************** */
|
||||
/* Stats */
|
||||
|
||||
/* Stats for Meshes */
|
||||
typedef struct MeshStatVis {
|
||||
char type;
|
||||
char _pad1[2];
|
||||
@@ -1228,7 +1293,13 @@ typedef struct ToolSettings {
|
||||
char gpencil_flags; /* flags/options for how the tool works */
|
||||
char gpencil_src; /* for main 3D view Grease Pencil, where data comes from */
|
||||
|
||||
char pad[4];
|
||||
char gpencil_v3d_align; /* stroke placement settings: 3D View */
|
||||
char gpencil_v2d_align; /* : General 2D Editor */
|
||||
char gpencil_seq_align; /* : Sequencer Preview */
|
||||
char gpencil_ima_align; /* : Image Editor */
|
||||
|
||||
/* Grease Pencil Sculpt */
|
||||
struct GP_BrushEdit_Settings gp_sculpt;
|
||||
|
||||
/* Image Paint (8 byttse aligned please!) */
|
||||
struct ImagePaintSettings imapaint;
|
||||
@@ -1917,7 +1988,12 @@ typedef enum ImagePaintMode {
|
||||
#define EDGE_MODE_TAG_FREESTYLE 5
|
||||
|
||||
/* toolsettings->gpencil_flags */
|
||||
#define GP_TOOL_FLAG_PAINTSESSIONS_ON (1<<0)
|
||||
typedef enum eGPencil_Flags {
|
||||
/* "Continuous Drawing" - The drawing operator enters a mode where multiple strokes can be drawn */
|
||||
GP_TOOL_FLAG_PAINTSESSIONS_ON = (1 << 0),
|
||||
/* When creating new frames, the last frame gets used as the basis for the new one */
|
||||
GP_TOOL_FLAG_RETAIN_LAST = (1 << 1),
|
||||
} eGPencil_Flags;
|
||||
|
||||
/* toolsettings->gpencil_src */
|
||||
typedef enum eGPencil_Source_3D {
|
||||
@@ -1925,6 +2001,22 @@ typedef enum eGPencil_Source_3D {
|
||||
GP_TOOL_SOURCE_OBJECT = 1
|
||||
} eGPencil_Source_3d;
|
||||
|
||||
/* toolsettings->gpencil_*_align - Stroke Placement mode flags */
|
||||
typedef enum eGPencil_Placement_Flags {
|
||||
/* New strokes are added in viewport/data space (i.e. not screen space) */
|
||||
GP_PROJECT_VIEWSPACE = (1 << 0),
|
||||
|
||||
/* Viewport space, but relative to render canvas (Sequencer Preview Only) */
|
||||
GP_PROJECT_CANVAS = (1 << 1),
|
||||
|
||||
/* Project into the screen's Z values */
|
||||
GP_PROJECT_DEPTH_VIEW = (1 << 2),
|
||||
GP_PROJECT_DEPTH_STROKE = (1 << 3),
|
||||
|
||||
/* "Use Endpoints" */
|
||||
GP_PROJECT_DEPTH_STROKE_ENDPOINTS = (1 << 4),
|
||||
} eGPencil_Placement_Flags;
|
||||
|
||||
/* toolsettings->particle flag */
|
||||
#define PE_KEEP_LENGTHS 1
|
||||
#define PE_LOCK_FIRST 2
|
||||
|
@@ -259,6 +259,8 @@ extern StructRNA RNA_GPencilFrame;
|
||||
extern StructRNA RNA_GPencilLayer;
|
||||
extern StructRNA RNA_GPencilStroke;
|
||||
extern StructRNA RNA_GPencilStrokePoint;
|
||||
extern StructRNA RNA_GPencilSculptSettings;
|
||||
extern StructRNA RNA_GPencilSculptBrush;
|
||||
extern StructRNA RNA_GameBooleanProperty;
|
||||
extern StructRNA RNA_GameFloatProperty;
|
||||
extern StructRNA RNA_GameIntProperty;
|
||||
|
@@ -111,6 +111,8 @@ extern EnumPropertyItem rna_enum_brush_sculpt_tool_items[];
|
||||
extern EnumPropertyItem rna_enum_brush_vertex_tool_items[];
|
||||
extern EnumPropertyItem rna_enum_brush_image_tool_items[];
|
||||
|
||||
extern EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[];
|
||||
|
||||
extern EnumPropertyItem rna_enum_symmetrize_direction_items[];
|
||||
|
||||
extern EnumPropertyItem rna_enum_texture_type_items[];
|
||||
|
@@ -495,6 +495,14 @@ static void rna_def_dopesheet(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Display Grease Pencil", "Include visualization of Grease Pencil related animation data and frames");
|
||||
RNA_def_property_ui_icon(prop, ICON_GREASEPENCIL, 0);
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
|
||||
|
||||
/* GPencil Mode Settings */
|
||||
prop = RNA_def_property(srna, "show_gpencil_3d_only", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "filterflag", ADS_FILTER_GP_3DONLY);
|
||||
RNA_def_property_ui_text(prop, "Active Scene Only",
|
||||
"Only show Grease Pencil datablocks used as part of the active scene");
|
||||
RNA_def_property_ui_icon(prop, ICON_SCENE_DATA, 0);
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
|
||||
}
|
||||
|
||||
static void rna_def_action_group(BlenderRNA *brna)
|
||||
|
@@ -50,11 +50,46 @@
|
||||
|
||||
#include "BKE_gpencil.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
|
||||
static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
|
||||
}
|
||||
|
||||
static void rna_GPencil_editmode_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
/* Notify all places where GPencil data lives that the editing state is different */
|
||||
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
|
||||
WM_main_add_notifier(NC_SCENE | ND_MODE, NULL);
|
||||
}
|
||||
|
||||
static void rna_GPencil_onion_skinning_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)ptr->id.data;
|
||||
bGPDlayer *gpl;
|
||||
bool enabled = false;
|
||||
|
||||
/* Ensure that the datablock's onionskinning toggle flag
|
||||
* stays in sync with the status of the actual layers
|
||||
*/
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
if (gpl->flag & GP_LAYER_ONIONSKIN) {
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
gpd->flag |= GP_DATA_SHOW_ONIONSKINS;
|
||||
else
|
||||
gpd->flag &= ~GP_DATA_SHOW_ONIONSKINS;
|
||||
|
||||
|
||||
/* Now do standard updates... */
|
||||
rna_GPencil_update(bmain, scene, ptr);
|
||||
}
|
||||
|
||||
static char *rna_GPencilLayer_path(PointerRNA *ptr)
|
||||
{
|
||||
bGPDlayer *gpl = (bGPDlayer *)ptr->data;
|
||||
@@ -201,6 +236,30 @@ static void rna_GPencilLayer_info_set(PointerRNA *ptr, const char *value)
|
||||
BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info));
|
||||
}
|
||||
|
||||
static void rna_GPencil_use_onion_skinning_set(PointerRNA *ptr, const int value)
|
||||
{
|
||||
bGPdata *gpd = ptr->id.data;
|
||||
bGPDlayer *gpl;
|
||||
|
||||
/* set new value */
|
||||
if (value) {
|
||||
/* enable on active layer (it's the one that's most likely to be of interest right now) */
|
||||
gpl = gpencil_layer_getactive(gpd);
|
||||
if (gpl) {
|
||||
gpl->flag |= GP_LAYER_ONIONSKIN;
|
||||
}
|
||||
|
||||
gpd->flag |= GP_DATA_SHOW_ONIONSKINS;
|
||||
}
|
||||
else {
|
||||
/* disable on all layers - allowa quickly turning them all off, without having to check */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
gpl->flag &= ~GP_LAYER_ONIONSKIN;
|
||||
}
|
||||
|
||||
gpd->flag &= ~GP_DATA_SHOW_ONIONSKINS;
|
||||
}
|
||||
}
|
||||
|
||||
static bGPDstroke *rna_GPencil_stroke_point_find_stroke(const bGPdata *gpd, const bGPDspoint *pt, bGPDlayer **r_gpl, bGPDframe **r_gpf)
|
||||
{
|
||||
@@ -701,7 +760,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
prop = RNA_def_property(srna, "use_onion_skinning", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_ONIONSKIN);
|
||||
RNA_def_property_ui_text(prop, "Onion Skinning", "Ghost frames on either side of frame");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_onion_skinning_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ghost_before_range", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "gstep");
|
||||
@@ -849,14 +908,6 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
|
||||
PropertyRNA *prop;
|
||||
FunctionRNA *func;
|
||||
|
||||
static EnumPropertyItem draw_mode_items[] = {
|
||||
{GP_DATA_VIEWALIGN, "CURSOR", 0, "Cursor", "Draw stroke at the 3D cursor"},
|
||||
{0, "VIEW", 0, "View", "Stick stroke to the view "}, /* weird, GP_DATA_VIEWALIGN is inverted */
|
||||
{GP_DATA_VIEWALIGN | GP_DATA_DEPTH_VIEW, "SURFACE", 0, "Surface", "Stick stroke to surfaces"},
|
||||
{GP_DATA_VIEWALIGN | GP_DATA_DEPTH_STROKE, "STROKE", 0, "Stroke", "Stick stroke to other strokes"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
srna = RNA_def_struct(brna, "GreasePencil", "ID");
|
||||
RNA_def_struct_sdna(srna, "bGPdata");
|
||||
RNA_def_struct_ui_text(srna, "Grease Pencil", "Freehand annotation sketchbook");
|
||||
@@ -873,21 +924,17 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
|
||||
rna_def_animdata_common(srna);
|
||||
|
||||
/* Flags */
|
||||
prop = RNA_def_property(srna, "draw_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
|
||||
RNA_def_property_enum_items(prop, draw_mode_items);
|
||||
RNA_def_property_ui_text(prop, "Draw Mode", "");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_stroke_endpoints", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_DEPTH_STROKE_ENDPOINTS);
|
||||
RNA_def_property_ui_text(prop, "Only Endpoints", "Only use the first and last parts of the stroke for snapping");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_stroke_edit_mode", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_EDITMODE);
|
||||
RNA_def_property_ui_text(prop, "Stroke Edit Mode", "Enable alternative keymap to make editing stroke points easier");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, "rna_GPencil_update");
|
||||
RNA_def_property_ui_text(prop, "Stroke Edit Mode", "Edit Grease Pencil strokes instead of viewport data");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, "rna_GPencil_editmode_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_onion_skinning", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_SHOW_ONIONSKINS);
|
||||
RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_use_onion_skinning_set");
|
||||
RNA_def_property_ui_text(prop, "Onion Skins",
|
||||
"Show ghosts of the frames before and after the current frame, toggle to enable on active layer or disable all");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
/* API Functions */
|
||||
func = RNA_def_function(srna, "clear", "rna_GPencil_clear");
|
||||
|
@@ -69,6 +69,7 @@ EnumPropertyItem rna_enum_object_mode_items[] = {
|
||||
{OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
|
||||
{OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
|
||||
{OB_MODE_PARTICLE_EDIT, "PARTICLE_EDIT", ICON_PARTICLEMODE, "Particle Edit", ""},
|
||||
{OB_MODE_GPENCIL, "GPENCIL_EDIT", ICON_GREASEPENCIL, "Edit Strokes", "Edit Grease Pencil Strokes"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
@@ -2057,11 +2057,19 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
||||
"unless the active object already has Grease Pencil data (i.e. for old files)"},
|
||||
{GP_TOOL_SOURCE_OBJECT, "OBJECT", 0, "Object",
|
||||
"Grease Pencil data-blocks attached to the active object are used "
|
||||
"(required using pre 2.73 add-ons, e.g. BSurfaces)"},
|
||||
"(required when using pre 2.73 add-ons, e.g. BSurfaces)"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
|
||||
static EnumPropertyItem gpencil_stroke_placement_items[] = {
|
||||
{GP_PROJECT_VIEWSPACE, "CURSOR", 0, "Cursor", "Draw stroke at the 3D cursor"},
|
||||
{0, "VIEW", 0, "View", "Stick stroke to the view "}, /* weird, GP_PROJECT_VIEWALIGN is inverted */
|
||||
{GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_VIEW, "SURFACE", 0, "Surface", "Stick stroke to surfaces"},
|
||||
{GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_STROKE, "STROKE", 0, "Stroke", "Stick stroke to other strokes"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
srna = RNA_def_struct(brna, "ToolSettings", NULL);
|
||||
RNA_def_struct_path_func(srna, "rna_ToolSettings_path");
|
||||
RNA_def_struct_ui_text(srna, "Tool Settings", "");
|
||||
@@ -2269,12 +2277,19 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
|
||||
|
||||
/* Grease Pencil */
|
||||
prop = RNA_def_property(srna, "use_grease_pencil_sessions", PROP_BOOLEAN, PROP_NONE);
|
||||
prop = RNA_def_property(srna, "use_gpencil_continuous_drawing", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_PAINTSESSIONS_ON);
|
||||
RNA_def_property_ui_text(prop, "Use Sketching Sessions",
|
||||
RNA_def_property_ui_text(prop, "Use Continuous Drawing",
|
||||
"Allow drawing multiple strokes at a time with Grease Pencil");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* xxx: need toolbar to be redrawn... */
|
||||
|
||||
prop = RNA_def_property(srna, "use_gpencil_additive_drawing", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_RETAIN_LAST);
|
||||
RNA_def_property_ui_text(prop, "Use Additive Drawing",
|
||||
"When creating new frames, the strokes from the previous/active frame "
|
||||
"are included as the basis for the new one");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "grease_pencil_source", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_src");
|
||||
RNA_def_property_enum_items(prop, gpencil_source_3d_items);
|
||||
@@ -2282,6 +2297,45 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
||||
"Datablock where active Grease Pencil data is found from");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "gpencil_sculpt", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt");
|
||||
RNA_def_property_struct_type(prop, "GPencilSculptSettings");
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", "");
|
||||
|
||||
/* Grease Pencil - 3D View Stroke Placement */
|
||||
prop = RNA_def_property(srna, "gpencil_stroke_placement_view3d", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v3d_align");
|
||||
RNA_def_property_enum_items(prop, gpencil_stroke_placement_items);
|
||||
RNA_def_property_ui_text(prop, "Stroke Placement (3D View)", "");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_gpencil_stroke_endpoints", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "gpencil_v3d_align", GP_PROJECT_DEPTH_STROKE_ENDPOINTS);
|
||||
RNA_def_property_ui_text(prop, "Only Endpoints", "Only use the first and last parts of the stroke for snapping");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
/* Grease Pencil - 2D Views Stroke Placement */
|
||||
prop = RNA_def_property(srna, "gpencil_stroke_placement_view2d", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v2d_align");
|
||||
RNA_def_property_enum_items(prop, gpencil_stroke_placement_items);
|
||||
RNA_def_property_ui_text(prop, "Stroke Placement (2D View)", "");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
/* Grease Pencil - Sequencer Preview Stroke Placement */
|
||||
prop = RNA_def_property(srna, "gpencil_stroke_placement_sequencer_preview", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_seq_align");
|
||||
RNA_def_property_enum_items(prop, gpencil_stroke_placement_items);
|
||||
RNA_def_property_ui_text(prop, "Stroke Placement (Sequencer Preview)", "");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
/* Grease Pencil - Image Editor Stroke Placement */
|
||||
prop = RNA_def_property(srna, "gpencil_stroke_placement_image_editor", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_ima_align");
|
||||
RNA_def_property_enum_items(prop, gpencil_stroke_placement_items);
|
||||
RNA_def_property_ui_text(prop, "Stroke Placement (Image Editor)", "");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
|
||||
/* Auto Keying */
|
||||
prop = RNA_def_property(srna, "use_keyframe_insert_auto", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "autokey_mode", AUTOKEY_ON);
|
||||
|
@@ -60,6 +60,20 @@ static EnumPropertyItem particle_edit_hair_brush_items[] = {
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = {
|
||||
{GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points"},
|
||||
{GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes"},
|
||||
{GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle"},
|
||||
{GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them"},
|
||||
{GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush"},
|
||||
{GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush"},
|
||||
{GP_EDITBRUSH_TYPE_RANDOMISE, "RANDOMISE", 0, "Randomise", "Introduce jitter/randomness into strokes"},
|
||||
//{GP_EDITBRUSH_TYPE_SUBDIVIDE, "SUBDIVIDE", 0, "Subdivide", "Increase point density for higher resolution strokes when zoomed in"},
|
||||
//{GP_EDITBRUSH_TYPE_SIMPLIFY, "SIMPLIFY", 0, "Simplify", "Reduce density of stroke points"},
|
||||
{GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
EnumPropertyItem rna_enum_symmetrize_direction_items[] = {
|
||||
{BMO_SYMMETRIZE_NEGATIVE_X, "NEGATIVE_X", 0, "-X to +X", ""},
|
||||
{BMO_SYMMETRIZE_POSITIVE_X, "POSITIVE_X", 0, "+X to -X", ""},
|
||||
@@ -359,6 +373,30 @@ static int rna_ImaPaint_detect_data(ImagePaintSettings *imapaint)
|
||||
{
|
||||
return imapaint->missing_data == 0;
|
||||
}
|
||||
|
||||
|
||||
static PointerRNA rna_GPencilSculptSettings_brush_get(PointerRNA *ptr)
|
||||
{
|
||||
GP_BrushEdit_Settings *gset = (GP_BrushEdit_Settings *)ptr->data;
|
||||
GP_EditBrush_Data *brush = NULL;
|
||||
|
||||
if ((gset->brushtype >= 0) && (gset->brushtype < TOT_GP_EDITBRUSH_TYPES))
|
||||
brush = &gset->brush[gset->brushtype];
|
||||
|
||||
return rna_pointer_inherit_refine(ptr, &RNA_GPencilSculptBrush, brush);
|
||||
}
|
||||
|
||||
static char *rna_GPencilSculptSettings_path(PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
return BLI_strdup("tool_settings.gpencil_sculpt");
|
||||
}
|
||||
|
||||
static char *rna_GPencilSculptBrush_path(PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
return BLI_strdup("tool_settings.gpencil_sculpt.brush");
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
static void rna_def_paint_curve(BlenderRNA *brna)
|
||||
@@ -945,6 +983,72 @@ static void rna_def_particle_edit(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Curve", "");
|
||||
}
|
||||
|
||||
static void rna_def_gpencil_sculpt(BlenderRNA *brna)
|
||||
{
|
||||
static EnumPropertyItem prop_direction_items[]= {
|
||||
{0, "ADD", 0, "Add", "Add effect of brush"},
|
||||
{GP_EDITBRUSH_FLAG_INVERT, "SUBTRACT", 0, "Subtract", "Subtract effect of brush"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* == Settings == */
|
||||
srna = RNA_def_struct(brna, "GPencilSculptSettings", NULL);
|
||||
RNA_def_struct_sdna(srna, "GP_BrushEdit_Settings");
|
||||
RNA_def_struct_path_func(srna, "rna_GPencilSculptSettings_path");
|
||||
RNA_def_struct_ui_text(srna, "GPencil Sculpt Settings", "Properties for Grease Pencil stroke sculpting tool");
|
||||
|
||||
prop = RNA_def_property(srna, "tool", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "brushtype");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_sculpt_brush_items);
|
||||
RNA_def_property_ui_text(prop, "Tool", "");
|
||||
|
||||
prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "GPencilSculptBrush");
|
||||
RNA_def_property_pointer_funcs(prop, "rna_GPencilSculptSettings_brush_get", NULL, NULL, NULL);
|
||||
RNA_def_property_ui_text(prop, "Brush", "");
|
||||
|
||||
prop = RNA_def_property(srna, "use_select_mask", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_SELECT_MASK);
|
||||
RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke points");
|
||||
RNA_def_property_ui_icon(prop, ICON_VERTEXSEL, 0); // FIXME: this needs a custom icon
|
||||
|
||||
|
||||
/* brush */
|
||||
srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL);
|
||||
RNA_def_struct_sdna(srna, "GP_EditBrush_Data");
|
||||
RNA_def_struct_path_func(srna, "rna_GPencilSculptBrush_path");
|
||||
RNA_def_struct_ui_text(srna, "GPencil Sculpt Brush", "Stroke editing brush");
|
||||
|
||||
prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL);
|
||||
RNA_def_property_range(prop, 1, MAX_BRUSH_PIXEL_RADIUS);
|
||||
RNA_def_property_ui_range(prop, 1, 100, 10, 3); // XXX: too big
|
||||
RNA_def_property_ui_text(prop, "Radius", "Radius of the brush in pixels");
|
||||
|
||||
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_range(prop, 0.001, 1.0);
|
||||
RNA_def_property_ui_text(prop, "Strength", "Brush strength");
|
||||
|
||||
prop = RNA_def_property(srna, "use_pressure_strength", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_EDITBRUSH_FLAG_USE_PRESSURE);
|
||||
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
|
||||
RNA_def_property_ui_text(prop, "Strength Pressure", "Enable tablet pressure sensitivity for strength");
|
||||
|
||||
prop = RNA_def_property(srna, "use_falloff", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_EDITBRUSH_FLAG_USE_FALLOFF);
|
||||
RNA_def_property_ui_text(prop, "Use Falloff", "Strength of brush decays with distance from cursor");
|
||||
|
||||
prop = RNA_def_property(srna, "affect_pressure", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE);
|
||||
RNA_def_property_ui_text(prop, "Affect Pressure", "Affect pressure values as well when smoothing strokes");
|
||||
|
||||
prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
|
||||
RNA_def_property_enum_items(prop, prop_direction_items);
|
||||
RNA_def_property_ui_text(prop, "Direction", "");
|
||||
}
|
||||
|
||||
void RNA_def_sculpt_paint(BlenderRNA *brna)
|
||||
{
|
||||
/* *** Non-Animated *** */
|
||||
@@ -956,6 +1060,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
|
||||
rna_def_vertex_paint(brna);
|
||||
rna_def_image_paint(brna);
|
||||
rna_def_particle_edit(brna);
|
||||
rna_def_gpencil_sculpt(brna);
|
||||
RNA_define_animate_sdna(true);
|
||||
}
|
||||
|
||||
|
@@ -86,6 +86,9 @@ static EnumPropertyItem event_mouse_type_items[] = {
|
||||
{ACTIONMOUSE, "ACTIONMOUSE", 0, "Action", ""},
|
||||
{SELECTMOUSE, "SELECTMOUSE", 0, "Select", ""},
|
||||
{0, "", 0, NULL, NULL},
|
||||
{TABLET_STYLUS, "PEN", 0, "Pen", ""},
|
||||
{TABLET_ERASER, "ERASER", 0, "Eraser", ""},
|
||||
{0, "", 0, NULL, NULL},
|
||||
{MOUSEMOVE, "MOUSEMOVE", 0, "Move", ""},
|
||||
{MOUSEPAN, "TRACKPADPAN", 0, "Mouse/Trackpad Pan", ""},
|
||||
{MOUSEZOOM, "TRACKPADZOOM", 0, "Mouse/Trackpad Zoom", ""},
|
||||
@@ -180,6 +183,9 @@ EnumPropertyItem rna_enum_event_type_items[] = {
|
||||
{ACTIONMOUSE, "ACTIONMOUSE", 0, "Action Mouse", "MBA"},
|
||||
{SELECTMOUSE, "SELECTMOUSE", 0, "Select Mouse", "MBS"},
|
||||
{0, "", 0, NULL, NULL},
|
||||
{TABLET_STYLUS, "PEN", 0, "Pen", ""},
|
||||
{TABLET_ERASER, "ERASER", 0, "Eraser", ""},
|
||||
{0, "", 0, NULL, NULL},
|
||||
{MOUSEMOVE, "MOUSEMOVE", 0, "Mouse Move", "MsMov"},
|
||||
{INBETWEEN_MOUSEMOVE, "INBETWEEN_MOUSEMOVE", 0, "In-between Move", "MsSubMov"},
|
||||
{MOUSEPAN, "TRACKPADPAN", 0, "Mouse/Trackpad Pan", "MsPan"},
|
||||
|
@@ -1538,8 +1538,24 @@ static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
|
||||
if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) return 1;
|
||||
}
|
||||
|
||||
if (kmitype != KM_ANY)
|
||||
if (winevent->type != kmitype) return 0;
|
||||
if (kmitype != KM_ANY) {
|
||||
if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) {
|
||||
const wmTabletData *wmtab = winevent->tablet_data;
|
||||
|
||||
if (wmtab == NULL)
|
||||
return 0;
|
||||
else if (winevent->type != LEFTMOUSE) /* tablet events can occur on hover + keypress */
|
||||
return 0;
|
||||
else if ((kmitype == TABLET_STYLUS) && (wmtab->Active != EVT_TABLET_STYLUS))
|
||||
return 0;
|
||||
else if ((kmitype == TABLET_ERASER) && (wmtab->Active != EVT_TABLET_ERASER))
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (winevent->type != kmitype)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (kmi->val != KM_ANY)
|
||||
if (winevent->val != kmi->val) return 0;
|
||||
|
@@ -513,6 +513,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
|
||||
free_anim_copybuf();
|
||||
free_anim_drivers_copybuf();
|
||||
free_fmodifiers_copybuf();
|
||||
ED_gpencil_anim_copybuf_free();
|
||||
ED_gpencil_strokes_copybuf_free();
|
||||
ED_clipboard_posebuf_free();
|
||||
BKE_node_clipboard_clear();
|
||||
|
@@ -202,6 +202,9 @@ int WM_keymap_map_type_get(wmKeyMapItem *kmi)
|
||||
if (kmi->type == KM_TEXTINPUT) {
|
||||
return KMI_TYPE_TEXTINPUT;
|
||||
}
|
||||
if (ELEM(kmi->type, TABLET_STYLUS, TABLET_ERASER)) {
|
||||
return KMI_TYPE_MOUSE;
|
||||
}
|
||||
return KMI_TYPE_KEYBOARD;
|
||||
}
|
||||
|
||||
|
@@ -92,6 +92,10 @@ enum {
|
||||
WM_IME_COMPOSITE_EVENT = 0x0015,
|
||||
/* IME event, GHOST_kEventImeCompositionEnd in ghost */
|
||||
WM_IME_COMPOSITE_END = 0x0016,
|
||||
|
||||
/* Tablet/Pen Specific Events */
|
||||
TABLET_STYLUS = 0x001a,
|
||||
TABLET_ERASER = 0x001b,
|
||||
|
||||
/* *** Start of keyboard codes. *** */
|
||||
|
||||
|
Reference in New Issue
Block a user