Compare commits
156 Commits
cmake-lib-
...
GPencil_Ed
Author | SHA1 | Date | |
---|---|---|---|
a88a9a74bc | |||
a783cfddb2 | |||
9eefc574a1 | |||
ba312f8caa | |||
f2fd1db85d | |||
703c302641 | |||
b75ecc8769 | |||
1d58aedf6f | |||
351e6afa81 | |||
ef0de17caa | |||
ca98aca565 | |||
defd3a6ebf | |||
018a7031a1 | |||
8c143929b9 | |||
cb4bfc74b7 | |||
16d0ded827 | |||
45dbc8aa5a | |||
84a1b8c179 | |||
09790ca8cc | |||
d7b27b8668 | |||
bdb33d3c6e | |||
e85ca034a3 | |||
31e69403fc | |||
4791f0cd7b | |||
e56030c874 | |||
57244603dd | |||
09aa72a5b7 | |||
bf4d8648bb | |||
9e17304ffb | |||
89bb9b46a4 | |||
189e89769f | |||
4c5439075b | |||
c4e17cfa1e | |||
22971615c2 | |||
ce4639b2c7 | |||
3926d10f0f | |||
9686e48bab | |||
426ed70009 | |||
bb69343c2a | |||
4d30667521 | |||
b9e55fc315 | |||
97a6100279 | |||
f07a7a5a4b | |||
0be82251b3 | |||
85837937a0 | |||
8fe991336f | |||
bbc54294df | |||
a2c8d5beb2 | |||
279ad06e04 | |||
8f14778a59 | |||
027824714e | |||
dfc7423a26 | |||
e33691dc59 | |||
f193652814 | |||
d404a45df0 | |||
7a80a76b0d | |||
24380454da | |||
365b38799a | |||
f11a5ce8c1 | |||
37c08c1e9f | |||
32db28d08a | |||
cf3ce9fbfc | |||
0a0c2d48d5 | |||
ee89e770e3 | |||
3eca27cdd1 | |||
527d1593e8 | |||
79236d5b81 | |||
fa9a41cd02 | |||
86749a5f7c | |||
23bbab6bdc | |||
f01a10d07d | |||
c80972286c | |||
1219070c55 | |||
aadb8305b4 | |||
516f551052 | |||
5035eef260 | |||
3d5317febd | |||
b800500814 | |||
4793302674 | |||
bb45d48c1b | |||
ead565678b | |||
2c1bcad26b | |||
ef71a77e56 | |||
3540bebf23 | |||
e14b8711b3 | |||
41e5c6198f | |||
1201c20558 | |||
f0170d035b | |||
da5a4a2f26 | |||
388baf3b70 | |||
b09ac229ca | |||
efb32c7169 | |||
bd7b692f29 | |||
c8a0e91ac2 | |||
b62677210e | |||
e9ae8a382f | |||
5de47a3d6e | |||
c7eee31838 | |||
c6110bf156 | |||
34fe61ebcb | |||
76007990d3 | |||
c3c22afd9e | |||
6ed6dd1301 | |||
c704c69b7e | |||
cd540f3937 | |||
ec8ad8d0c4 | |||
e3fd2fe14b | |||
8b445fba5c | |||
0e3ac25de0 | |||
d5d2a289db | |||
c39e9e3534 | |||
3dd6dbdc6f | |||
8b5a6e9323 | |||
f94c969193 | |||
7e61d1fc4f | |||
8abecb7a50 | |||
3437544b88 | |||
d88288b209 | |||
4f6c99a53c | |||
f4a1ed4351 | |||
38308baf1b | |||
7d047917b5 | |||
ec5f0d27b5 | |||
e2b872ccc8 | |||
ab62df4837 | |||
779826d295 | |||
9921e72b72 | |||
c10359cb81 | |||
ae65857471 | |||
97f9b3c05f | |||
3e0a73e45a | |||
b42c5a05d5 | |||
9ef589cbcc | |||
72c19c2148 | |||
245c5d3d3a | |||
e744bc03e1 | |||
5be7c40552 | |||
50d88a316d | |||
fb883b3262 | |||
80c4e23531 | |||
625aeb5900 | |||
9dd0881abb | |||
3cfeeeb2ad | |||
5222194967 | |||
0b62328bb5 | |||
159caf73da | |||
e456c54fa4 | |||
72e18c873e | |||
b616bea15c | |||
e1a65d804d | |||
f08fdc0b33 | |||
75b708f702 | |||
914efb853d | |||
e4d1fe7c65 | |||
06e188adb7 | |||
0c346fab2c |
@@ -32,7 +32,10 @@ KM_HIERARCHY = [
|
||||
('View2D', 'EMPTY', 'WINDOW', []), # view 2d navigation (per region)
|
||||
('View2D Buttons List', 'EMPTY', 'WINDOW', []), # view 2d with buttons navigation
|
||||
('Header', 'EMPTY', 'WINDOW', []), # header stuff (per region)
|
||||
('Grease Pencil', 'EMPTY', 'WINDOW', []), # grease pencil stuff (per region)
|
||||
|
||||
('Grease Pencil', 'EMPTY', 'WINDOW', [ # grease pencil stuff (per region)
|
||||
('Grease Pencil Stroke Edit Mode', 'EMPTY', 'WINDOW', []),
|
||||
]),
|
||||
|
||||
('3D View', 'VIEW_3D', 'WINDOW', [ # view 3d navigation and generic stuff (select, transform)
|
||||
('Object Mode', 'EMPTY', 'WINDOW', []),
|
||||
|
@@ -19,11 +19,35 @@
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
class GreasePencilPanel():
|
||||
import bpy
|
||||
from bpy.types import Menu, UIList
|
||||
|
||||
|
||||
def gpencil_stroke_placement_settings(context, layout, gpd):
|
||||
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')
|
||||
|
||||
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 = col.row(align=False)
|
||||
row.active = gpd.draw_mode in ('SURFACE', 'STROKE')
|
||||
row.prop(gpd, "use_stroke_endpoints")
|
||||
|
||||
|
||||
class GreasePencilDrawingToolsPanel():
|
||||
# subclass must set
|
||||
# bl_space_type = 'IMAGE_EDITOR'
|
||||
# bl_region_type = 'TOOLS'
|
||||
bl_label = "Grease Pencil"
|
||||
bl_category = "Grease Pencil"
|
||||
bl_region_type = 'TOOLS'
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
@@ -31,19 +55,413 @@ class GreasePencilPanel():
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
||||
col.label(text="Draw:")
|
||||
row = col.row(align=True)
|
||||
row.operator("gpencil.draw", text="Draw").mode = 'DRAW'
|
||||
row.operator("gpencil.draw", text="Line").mode = 'DRAW_STRAIGHT'
|
||||
|
||||
row = col.row(align=True)
|
||||
row.operator("gpencil.draw", text="Poly").mode = 'DRAW_POLY'
|
||||
row.operator("gpencil.draw", text="Erase").mode = 'ERASER'
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(context.tool_settings, "use_grease_pencil_sessions")
|
||||
row.operator("gpencil.draw", text="Line").mode = 'DRAW_STRAIGHT'
|
||||
row.operator("gpencil.draw", text="Poly").mode = 'DRAW_POLY'
|
||||
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(context.tool_settings, "use_grease_pencil_sessions", text="Continuous Drawing")
|
||||
|
||||
gpd = context.gpencil_data
|
||||
if gpd:
|
||||
col.separator()
|
||||
gpencil_stroke_placement_settings(context, col, gpd)
|
||||
|
||||
|
||||
if context.space_data.type == 'VIEW_3D':
|
||||
col.separator()
|
||||
col.separator()
|
||||
|
||||
col.label(text="Measure:")
|
||||
col.label(text="Tools:")
|
||||
col.operator("gpencil.convert", text="Convert...")
|
||||
col.operator("view3d.ruler")
|
||||
|
||||
|
||||
class GreasePencilStrokeEditPanel():
|
||||
# subclass must set
|
||||
# bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_label = "Edit Strokes"
|
||||
bl_category = "Grease Pencil"
|
||||
bl_region_type = 'TOOLS'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.gpencil_data is not None)
|
||||
|
||||
@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)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
|
||||
|
||||
col.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:")
|
||||
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.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.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
|
||||
|
||||
|
||||
###############################
|
||||
|
||||
class GPENCIL_PIE_tool_palette(Menu):
|
||||
"""A pie menu for quick access to Grease Pencil tools"""
|
||||
bl_label = "Grease Pencil Tools"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
gpd = context.gpencil_data
|
||||
|
||||
# W - Drawing Types
|
||||
col = pie.column()
|
||||
col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
|
||||
col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
|
||||
col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
|
||||
|
||||
# E - Eraser
|
||||
# XXX: needs a dedicated icon...
|
||||
col = pie.column()
|
||||
col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
|
||||
|
||||
# E - "Settings" Palette is included here too, since it needs to be in a stable position...
|
||||
if gpd and gpd.layers.active:
|
||||
col.separator()
|
||||
col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_PIE_settings_palette"
|
||||
|
||||
# Editing tools
|
||||
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')
|
||||
|
||||
# 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 = col.row(align=True)
|
||||
row.label("Proportional Edit:")
|
||||
row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
|
||||
row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
|
||||
|
||||
# NW - Select (Non-Modal)
|
||||
col = pie.column()
|
||||
col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
|
||||
col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
|
||||
|
||||
# NE - Select (Modal)
|
||||
col = pie.column()
|
||||
col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
|
||||
col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
|
||||
|
||||
# SW - Edit Tools
|
||||
col = pie.column()
|
||||
col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
|
||||
col.operator("gpencil.delete", icon='X', text="Delete...")
|
||||
|
||||
# SE - More Tools
|
||||
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')
|
||||
|
||||
|
||||
class GPENCIL_PIE_settings_palette(Menu):
|
||||
"""A pie menu for quick access to Grease Pencil settings"""
|
||||
bl_label = "Grease Pencil Settings"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(context.gpencil_data and context.active_gpencil_layer)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pie = layout.menu_pie()
|
||||
gpd = context.gpencil_data
|
||||
gpl = context.active_gpencil_layer
|
||||
|
||||
# W - Stroke draw settings
|
||||
col = pie.column(align=True)
|
||||
col.label(text="Stroke")
|
||||
col.prop(gpl, "color", text="")
|
||||
col.prop(gpl, "alpha", text="", slider=True)
|
||||
|
||||
# E - Fill draw settings
|
||||
col = pie.column(align=True)
|
||||
col.label(text="Fill")
|
||||
col.prop(gpl, "fill_color", text="")
|
||||
col.prop(gpl, "fill_alpha", text="", slider=True)
|
||||
|
||||
# S - Layer settings
|
||||
col = pie.column()
|
||||
col.prop(gpl, "line_width", slider=True)
|
||||
#col.prop(gpl, "use_volumetric_strokes")
|
||||
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.prop(gpl, "lock")
|
||||
row.prop(gpl, "hide")
|
||||
|
||||
|
||||
class GPENCIL_PIE_tools_more(Menu):
|
||||
"""A pie menu for accessing more Grease Pencil tools"""
|
||||
bl_label = "More Grease Pencil Tools"
|
||||
|
||||
@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()
|
||||
gpd = context.gpencil_data
|
||||
|
||||
pie.operator("gpencil.select_more", icon='ZOOMIN')
|
||||
pie.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("gpencil.convert", icon='OUTLINER_OB_CURVE')
|
||||
pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette"
|
||||
|
||||
###############################
|
||||
|
||||
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)
|
||||
gpl = item
|
||||
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
if gpl.lock:
|
||||
layout.active = False
|
||||
|
||||
split = layout.split(percentage=0.2)
|
||||
split.prop(gpl, "color", text="")
|
||||
split.prop(gpl, "info", text="", emboss=False)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(gpl, "lock", text="", emboss=False)
|
||||
row.prop(gpl, "hide", text="", emboss=False)
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
class GreasePencilDataPanel():
|
||||
# subclass must set
|
||||
# bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_label = "Grease Pencil"
|
||||
bl_region_type = 'UI'
|
||||
|
||||
@staticmethod
|
||||
def draw_header(self, context):
|
||||
self.layout.prop(context.space_data, "show_grease_pencil", text="")
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# owner of Grease Pencil data
|
||||
gpd_owner = context.gpencil_data_owner
|
||||
gpd = context.gpencil_data
|
||||
|
||||
# Owner Selector
|
||||
# XXX: add this for 3D view too
|
||||
if context.space_data.type == 'CLIP_EDITOR':
|
||||
layout.prop(context.space_data, "grease_pencil_source", expand=True)
|
||||
|
||||
# Grease Pencil data selector
|
||||
layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
|
||||
|
||||
# Grease Pencil data...
|
||||
if gpd:
|
||||
self.draw_layers(context, layout, gpd)
|
||||
|
||||
def draw_layers(self, context, layout, gpd):
|
||||
row = layout.row()
|
||||
|
||||
col = row.column()
|
||||
col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=5)
|
||||
|
||||
col = row.column()
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
|
||||
sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
|
||||
|
||||
gpl = context.active_gpencil_layer
|
||||
if gpl:
|
||||
col.separator()
|
||||
|
||||
sub = col.column(align=True)
|
||||
sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
|
||||
sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
|
||||
|
||||
if gpl:
|
||||
self.draw_layer(layout, gpl)
|
||||
|
||||
def draw_layer(self, layout, gpl):
|
||||
# layer settings
|
||||
split = layout.split(percentage=0.5)
|
||||
split.active = not gpl.lock
|
||||
|
||||
# Column 1 - Stroke
|
||||
col = split.column(align=True)
|
||||
col.label(text="Stroke:")
|
||||
col.prop(gpl, "color", text="")
|
||||
col.prop(gpl, "alpha", slider=True)
|
||||
|
||||
# Column 2 - Fill
|
||||
col = split.column(align=True)
|
||||
col.label(text="Fill:")
|
||||
col.prop(gpl, "fill_color", text="")
|
||||
col.prop(gpl, "fill_alpha", text="Opacity", slider=True)
|
||||
|
||||
# Options
|
||||
split = layout.split(percentage=0.5)
|
||||
split.active = not gpl.lock
|
||||
|
||||
col = split.column(align=True)
|
||||
col.prop(gpl, "line_width", slider=True)
|
||||
col.prop(gpl, "use_volumetric_strokes")
|
||||
|
||||
col = split.column(align=True)
|
||||
col.prop(gpl, "show_x_ray")
|
||||
|
||||
#if debug:
|
||||
# layout.prop(gpl, "show_points")
|
||||
|
||||
layout.separator()
|
||||
|
||||
# Full-Row - Frame Locking (and Delete Frame)
|
||||
row = layout.row(align=True)
|
||||
row.active = not gpl.lock
|
||||
|
||||
if gpl.active_frame:
|
||||
lock_status = "Locked" if gpl.lock_frame else "Unlocked"
|
||||
lock_label = "Frame: %d (%s)" % (gpl.active_frame.frame_number, lock_status)
|
||||
else:
|
||||
lock_label = "Lock Frame"
|
||||
row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
|
||||
row.operator("gpencil.active_frame_delete", text="", icon='X')
|
||||
|
||||
layout.separator()
|
||||
|
||||
# Onion skinning
|
||||
col = layout.column(align=True)
|
||||
col.active = not gpl.lock
|
||||
|
||||
row = col.row()
|
||||
row.prop(gpl, "use_onion_skinning")
|
||||
row.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR')
|
||||
|
||||
split = col.split(percentage = 0.5)
|
||||
split.active = gpl.use_onion_skinning
|
||||
|
||||
# - Before Frames
|
||||
sub = split.column(align=True)
|
||||
row = sub.row(align=True)
|
||||
row.active = gpl.use_ghost_custom_colors
|
||||
row.prop(gpl, "before_color", text="")
|
||||
sub.prop(gpl, "ghost_before_range", text="Before")
|
||||
|
||||
|
||||
# - After Frames
|
||||
sub = split.column(align=True)
|
||||
row = sub.row(align=True)
|
||||
row.active = gpl.use_ghost_custom_colors
|
||||
row.prop(gpl, "after_color", text="")
|
||||
sub.prop(gpl, "ghost_after_range", text="After")
|
||||
|
||||
|
||||
class GreasePencilToolsPanel():
|
||||
# subclass must set
|
||||
# bl_space_type = 'IMAGE_EDITOR'
|
||||
# bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_label = "Grease Pencil Settings"
|
||||
bl_region_type = 'UI'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.gpencil_data is not None)
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
gpd_owner = context.gpencil_data_owner
|
||||
gpd = context.gpencil_data
|
||||
|
||||
layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.label("Proportional Edit:")
|
||||
row = layout.row()
|
||||
row.prop(context.tool_settings, "proportional_edit", text="")
|
||||
row.prop(context.tool_settings, "proportional_edit_falloff", text="")
|
||||
|
||||
layout.separator()
|
||||
layout.separator()
|
||||
|
||||
gpencil_stroke_placement_settings(context, layout, gpd)
|
||||
|
@@ -21,7 +21,11 @@
|
||||
import bpy
|
||||
from bpy.types import Panel, Header, Menu, UIList
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
from bl_ui.properties_grease_pencil_common import GreasePencilPanel
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel,
|
||||
GreasePencilDataPanel
|
||||
)
|
||||
|
||||
|
||||
class CLIP_UL_tracking_objects(UIList):
|
||||
@@ -1050,12 +1054,6 @@ class CLIP_PT_tools_mask(MASK_PT_tools, Panel):
|
||||
# --- end mask ---
|
||||
|
||||
|
||||
class CLIP_PT_tools_grease_pencil(GreasePencilPanel, Panel):
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
bl_region_type = 'TOOLS'
|
||||
bl_category = "Grease Pencil"
|
||||
|
||||
|
||||
class CLIP_PT_footage(CLIP_PT_clip_view_panel, Panel):
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
@@ -1110,6 +1108,26 @@ class CLIP_PT_tools_scenesetup(Panel):
|
||||
layout.operator("clip.setup_tracking_scene")
|
||||
|
||||
|
||||
# Grease Pencil properties
|
||||
class CLIP_PT_grease_pencil(GreasePencilDataPanel, CLIP_PT_clip_view_panel, Panel):
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
# NOTE: this is just a wrapper around the generic GP Panel
|
||||
# But, this should only be visible in "clip" view
|
||||
|
||||
|
||||
# Grease Pencil drawing tools
|
||||
class CLIP_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
|
||||
|
||||
# Grease Pencil stroke editing tools
|
||||
class CLIP_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
|
||||
|
||||
class CLIP_MT_view(Menu):
|
||||
bl_label = "View"
|
||||
|
||||
|
@@ -91,6 +91,8 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False):
|
||||
row.prop(dopesheet, "show_speakers", text="")
|
||||
if bpy.data.linestyles:
|
||||
row.prop(dopesheet, "show_linestyles", text="")
|
||||
if bpy.data.grease_pencil:
|
||||
row.prop(dopesheet, "show_gpencil", text="")
|
||||
|
||||
|
||||
#######################################
|
||||
@@ -365,14 +367,16 @@ class DOPESHEET_MT_gpencil_frame(Menu):
|
||||
layout = self.layout
|
||||
|
||||
layout.menu("DOPESHEET_MT_key_transform", text="Transform")
|
||||
|
||||
#layout.operator_menu_enum("action.snap", "type", text="Snap")
|
||||
#layout.operator_menu_enum("action.mirror", "type", text="Mirror")
|
||||
layout.operator_menu_enum("action.snap", "type", text="Snap")
|
||||
layout.operator_menu_enum("action.mirror", "type", text="Mirror")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("action.duplicate")
|
||||
layout.operator("action.delete")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("action.keyframe_type")
|
||||
|
||||
#layout.separator()
|
||||
#layout.operator("action.copy")
|
||||
#layout.operator("action.paste")
|
||||
|
@@ -25,7 +25,11 @@ from bl_ui.properties_paint_common import (
|
||||
brush_texpaint_common,
|
||||
brush_mask_texture_settings,
|
||||
)
|
||||
from bl_ui.properties_grease_pencil_common import GreasePencilPanel
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel,
|
||||
GreasePencilDataPanel
|
||||
)
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
|
||||
|
||||
@@ -1149,10 +1153,21 @@ class IMAGE_PT_scope_sample(Panel):
|
||||
sub.prop(sima.scopes, "accuracy")
|
||||
|
||||
|
||||
class IMAGE_PT_tools_grease_pencil(GreasePencilPanel, Panel):
|
||||
# Grease Pencil properties
|
||||
class IMAGE_PT_grease_pencil(GreasePencilDataPanel, Panel):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
# NOTE: this is just a wrapper around the generic GP Panel
|
||||
|
||||
# Grease Pencil drawing tools
|
||||
class IMAGE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
|
||||
|
||||
# Grease Pencil stroke editing tools
|
||||
class IMAGE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'IMAGE_EDITOR'
|
||||
bl_region_type = 'TOOLS'
|
||||
bl_category = "Grease Pencil"
|
||||
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
|
@@ -19,6 +19,12 @@
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Header, Menu, Panel
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel,
|
||||
GreasePencilDataPanel,
|
||||
GreasePencilToolsPanel,
|
||||
)
|
||||
|
||||
|
||||
class NODE_HT_header(Header):
|
||||
@@ -439,6 +445,45 @@ class NODE_UL_interface_sockets(bpy.types.UIList):
|
||||
layout.template_node_socket(color)
|
||||
|
||||
|
||||
# Grease Pencil properties
|
||||
class NODE_PT_grease_pencil(GreasePencilDataPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
# NOTE: this is just a wrapper around the generic GP Panel
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
snode = context.space_data
|
||||
return snode is not None and snode.node_tree is not None
|
||||
|
||||
|
||||
class NODE_PT_grease_pencil_tools(GreasePencilToolsPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
# NOTE: this is just a wrapper around the generic GP tools panel
|
||||
# It contains access to some essential tools usually found only in
|
||||
# toolbar, but which may not necessarily be open
|
||||
|
||||
|
||||
# Tool Shelf ------------------
|
||||
|
||||
|
||||
# Grease Pencil drawing tools
|
||||
class NODE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'TOOLS'
|
||||
|
||||
|
||||
# Grease Pencil stroke editing tools
|
||||
class NODE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'TOOLS'
|
||||
|
||||
# -----------------------------
|
||||
|
||||
def node_draw_tree_view(layout, context):
|
||||
pass
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Header, Menu, Panel
|
||||
from bl_ui.properties_grease_pencil_common import GreasePencilDataPanel, GreasePencilToolsPanel
|
||||
from bpy.app.translations import pgettext_iface as iface_
|
||||
|
||||
|
||||
@@ -103,6 +104,17 @@ class SEQUENCER_HT_header(Header):
|
||||
row = layout.row()
|
||||
row.prop(st, "overlay_type", text="")
|
||||
|
||||
if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
|
||||
gpd = context.gpencil_data
|
||||
toolsettings = context.tool_settings
|
||||
|
||||
# Proportional editing
|
||||
if gpd and gpd.use_stroke_edit_mode:
|
||||
row = layout.row(align=True)
|
||||
row.prop(toolsettings, "proportional_edit", icon_only=True)
|
||||
if toolsettings.proportional_edit != 'DISABLED':
|
||||
row.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("render.opengl", text="", icon='RENDER_STILL').sequencer = True
|
||||
props = row.operator("render.opengl", text="", icon='RENDER_ANIMATION')
|
||||
@@ -1017,5 +1029,22 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
|
||||
col.prop(mod, "contrast")
|
||||
|
||||
|
||||
class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Output, Panel):
|
||||
bl_space_type = 'SEQUENCE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
# NOTE: this is just a wrapper around the generic GP Panel
|
||||
# But, it should only show up when there are images in the preview region
|
||||
|
||||
|
||||
class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel):
|
||||
bl_space_type = 'SEQUENCE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
# NOTE: this is just a wrapper around the generic GP tools panel
|
||||
# It contains access to some essential tools usually found only in
|
||||
# toolbar, which doesn't exist here...
|
||||
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
bpy.utils.register_module(__name__)
|
||||
|
@@ -19,6 +19,7 @@
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Header, Menu, Panel
|
||||
from bl_ui.properties_grease_pencil_common import GreasePencilDataPanel
|
||||
from bl_ui.properties_paint_common import UnifiedPaintPanel
|
||||
from bpy.app.translations import contexts as i18n_contexts
|
||||
|
||||
@@ -56,7 +57,12 @@ class VIEW3D_HT_header(Header):
|
||||
row.prop(view, "use_occlude_geometry", text="")
|
||||
|
||||
# Proportional editing
|
||||
if mode in {'EDIT', 'PARTICLE_EDIT'}:
|
||||
if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode:
|
||||
row = layout.row(align=True)
|
||||
row.prop(toolsettings, "proportional_edit", icon_only=True)
|
||||
if toolsettings.proportional_edit != 'DISABLED':
|
||||
row.prop(toolsettings, "proportional_edit_falloff", icon_only=True)
|
||||
elif mode in {'EDIT', 'PARTICLE_EDIT'}:
|
||||
row = layout.row(align=True)
|
||||
row.prop(toolsettings, "proportional_edit", icon_only=True)
|
||||
if toolsettings.proportional_edit != 'DISABLED':
|
||||
@@ -2703,6 +2709,12 @@ class VIEW3D_MT_edit_armature_roll(Menu):
|
||||
|
||||
# ********** Panel **********
|
||||
|
||||
class VIEW3D_PT_grease_pencil(GreasePencilDataPanel, Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
# NOTE: this is just a wrapper around the generic GP Panel
|
||||
|
||||
|
||||
class VIEW3D_PT_view3d_properties(Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
|
@@ -19,7 +19,10 @@
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Menu, Panel, UIList
|
||||
from bl_ui.properties_grease_pencil_common import GreasePencilPanel
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
GreasePencilDrawingToolsPanel,
|
||||
GreasePencilStrokeEditPanel
|
||||
)
|
||||
from bl_ui.properties_paint_common import (
|
||||
UnifiedPaintPanel,
|
||||
brush_texture_settings,
|
||||
@@ -1805,11 +1808,14 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, Panel):
|
||||
sub.prop(pe, "fade_frames", slider=True)
|
||||
|
||||
|
||||
# Grease Pencil tools
|
||||
class VIEW3D_PT_tools_grease_pencil(GreasePencilPanel, Panel):
|
||||
# Grease Pencil drawing tools
|
||||
class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
|
||||
|
||||
# Grease Pencil stroke editing tools
|
||||
class VIEW3D_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'TOOLS'
|
||||
bl_category = "Grease Pencil"
|
||||
|
||||
|
||||
# Note: moved here so that it's always in last position in 'Tools' panels!
|
||||
|
@@ -56,6 +56,9 @@ struct Text;
|
||||
struct ImBuf;
|
||||
struct EditBone;
|
||||
struct bPoseChannel;
|
||||
struct bGPdata;
|
||||
struct bGPDlayer;
|
||||
struct bGPDframe;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
struct SpaceText;
|
||||
@@ -275,6 +278,14 @@ struct bPoseChannel *CTX_data_active_pose_bone(const bContext *C);
|
||||
int CTX_data_selected_pose_bones(const bContext *C, ListBase *list);
|
||||
int CTX_data_visible_pose_bones(const bContext *C, ListBase *list);
|
||||
|
||||
struct bGPdata *CTX_data_gpencil_data(const bContext *C);
|
||||
struct bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C);
|
||||
struct bGPDframe *CTX_data_active_gpencil_frame(const bContext *C);
|
||||
int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list);
|
||||
int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
|
||||
int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -35,6 +35,7 @@ struct ListBase;
|
||||
struct bGPdata;
|
||||
struct bGPDlayer;
|
||||
struct bGPDframe;
|
||||
struct bGPDstroke;
|
||||
|
||||
/* ------------ Grease-Pencil API ------------------ */
|
||||
|
||||
@@ -43,6 +44,8 @@ void free_gpencil_frames(struct bGPDlayer *gpl);
|
||||
void free_gpencil_layers(struct ListBase *list);
|
||||
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 bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd, const char *name, int setactive);
|
||||
struct bGPdata *gpencil_data_addnew(const char name[]);
|
||||
|
@@ -100,6 +100,7 @@ bool id_type_can_have_animdata(ID *id)
|
||||
case ID_SCE:
|
||||
case ID_MC:
|
||||
case ID_MSK:
|
||||
case ID_GD:
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -1031,6 +1032,9 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u
|
||||
|
||||
/* line styles */
|
||||
ANIMDATA_IDS_CB(mainptr->linestyle.first);
|
||||
|
||||
/* grease pencil */
|
||||
ANIMDATA_IDS_CB(mainptr->gpencil.first);
|
||||
}
|
||||
|
||||
/* Fix all RNA-Paths throughout the database (directly access the Global.main version)
|
||||
@@ -1119,6 +1123,9 @@ void BKE_all_animdata_fix_paths_rename(ID *ref_id, const char *prefix, const cha
|
||||
/* linestyles */
|
||||
RENAMEFIX_ANIM_IDS(mainptr->linestyle.first);
|
||||
|
||||
/* grease pencil */
|
||||
RENAMEFIX_ANIM_IDS(mainptr->gpencil.first);
|
||||
|
||||
/* scenes */
|
||||
RENAMEFIX_ANIM_NODETREE_IDS(mainptr->scene.first, Scene);
|
||||
}
|
||||
@@ -2680,6 +2687,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Scene *scene, float ctime)
|
||||
/* linestyles */
|
||||
EVAL_ANIM_IDS(main->linestyle.first, ADT_RECALC_ANIM);
|
||||
|
||||
/* grease pencil */
|
||||
EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM);
|
||||
|
||||
/* objects */
|
||||
/* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets
|
||||
* this tagged by Depsgraph on framechange. This optimization means that objects
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "DNA_windowmanager_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_linestyle_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
@@ -1090,3 +1091,34 @@ int CTX_data_visible_pose_bones(const bContext *C, ListBase *list)
|
||||
{
|
||||
return ctx_data_collection_get(C, "visible_pose_bones", list);
|
||||
}
|
||||
|
||||
bGPdata *CTX_data_gpencil_data(const bContext *C)
|
||||
{
|
||||
return ctx_data_pointer_get(C, "gpencil_data");
|
||||
}
|
||||
|
||||
bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C)
|
||||
{
|
||||
return ctx_data_pointer_get(C, "active_gpencil_layer");
|
||||
}
|
||||
|
||||
bGPDframe *CTX_data_active_gpencil_frame(const bContext *C)
|
||||
{
|
||||
return ctx_data_pointer_get(C, "active_gpencil_frame");
|
||||
}
|
||||
|
||||
int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list)
|
||||
{
|
||||
return ctx_data_collection_get(C, "visible_gpencil_layers", list);
|
||||
}
|
||||
|
||||
int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list)
|
||||
{
|
||||
return ctx_data_collection_get(C, "editable_gpencil_layers", list);
|
||||
}
|
||||
|
||||
int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list)
|
||||
{
|
||||
return ctx_data_collection_get(C, "editable_gpencil_strokes", list);
|
||||
}
|
||||
|
||||
|
@@ -45,6 +45,7 @@
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_library.h"
|
||||
@@ -115,6 +116,12 @@ void BKE_gpencil_free(bGPdata *gpd)
|
||||
{
|
||||
/* free layers */
|
||||
free_gpencil_layers(&gpd->layers);
|
||||
|
||||
/* free animation data */
|
||||
if (gpd->adt) {
|
||||
BKE_free_animdata(&gpd->id);
|
||||
gpd->adt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------- Container Creation ---------- */
|
||||
@@ -307,6 +314,31 @@ bGPdata *gpencil_data_duplicate(bGPdata *src, bool internal_copy)
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* -------- GP-Stroke API --------- */
|
||||
|
||||
/* ensure selection status of stroke is in sync with its points */
|
||||
void gpencil_stroke_sync_selection(bGPDstroke *gps)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* error checking */
|
||||
if (gps == NULL)
|
||||
return;
|
||||
|
||||
/* we'll stop when we find the first selected point,
|
||||
* so initially, we must deselect
|
||||
*/
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
gps->flag |= GP_STROKE_SELECT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------- GP-Frame API ---------- */
|
||||
|
||||
/* delete the last stroke of the given frame */
|
||||
@@ -366,7 +398,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, short addnew)
|
||||
/* do not allow any changes to layer's active frame if layer is locked from changes
|
||||
* or if the layer has been set to stay on the current frame
|
||||
*/
|
||||
if (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_FRAMELOCK))
|
||||
if (gpl->flag & GP_LAYER_FRAMELOCK)
|
||||
return gpf;
|
||||
/* do not allow any changes to actframe if frame has painting tag attached to it */
|
||||
if (gpf->flag & GP_FRAME_PAINT)
|
||||
@@ -475,16 +507,23 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, short addnew)
|
||||
bool gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
|
||||
/* error checking */
|
||||
if (ELEM(NULL, gpl, gpf))
|
||||
return false;
|
||||
|
||||
|
||||
/* if this frame was active, make the previous frame active instead
|
||||
* since it's tricky to set active frame otherwise
|
||||
*/
|
||||
if (gpl->actframe == gpf)
|
||||
gpl->actframe = gpf->prev;
|
||||
else
|
||||
gpl->actframe = NULL;
|
||||
|
||||
/* free the frame and its data */
|
||||
changed = free_gpencil_strokes(gpf);
|
||||
BLI_freelinkN(&gpl->frames, gpf);
|
||||
gpl->actframe = NULL;
|
||||
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@@ -5732,6 +5732,21 @@ static void lib_link_windowmanager(FileData *fd, Main *main)
|
||||
|
||||
/* ****************** READ GREASE PENCIL ***************** */
|
||||
|
||||
/* relink's grease pencil data's refs */
|
||||
static void lib_link_gpencil(FileData *fd, Main *main)
|
||||
{
|
||||
bGPdata *gpd;
|
||||
|
||||
for (gpd = main->gpencil.first; gpd; gpd = gpd->id.next) {
|
||||
if (gpd->id.flag & LIB_NEED_LINK) {
|
||||
gpd->id.flag -= LIB_NEED_LINK;
|
||||
|
||||
if (gpd->adt)
|
||||
lib_link_animdata(fd, &gpd->id, gpd->adt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* relinks grease-pencil data - used for direct_link and old file linkage */
|
||||
static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
|
||||
{
|
||||
@@ -5743,6 +5758,10 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
|
||||
if (gpd == NULL)
|
||||
return;
|
||||
|
||||
/* relink animdata */
|
||||
gpd->adt = newdataadr(fd, gpd->adt);
|
||||
direct_link_animdata(fd, gpd->adt);
|
||||
|
||||
/* relink layers */
|
||||
link_list(fd, &gpd->layers);
|
||||
|
||||
@@ -7714,6 +7733,7 @@ static void lib_link_all(FileData *fd, Main *main)
|
||||
lib_link_movieclip(fd, main);
|
||||
lib_link_mask(fd, main);
|
||||
lib_link_linestyle(fd, main);
|
||||
lib_link_gpencil(fd, main);
|
||||
|
||||
lib_link_mesh(fd, main); /* as last: tpage images with users at zero */
|
||||
|
||||
@@ -8819,6 +8839,12 @@ static void expand_linestyle(FileData *fd, Main *mainvar, FreestyleLineStyle *li
|
||||
}
|
||||
}
|
||||
|
||||
static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd)
|
||||
{
|
||||
if (gpd->adt)
|
||||
expand_animdata(fd, mainvar, gpd->adt);
|
||||
}
|
||||
|
||||
void BLO_main_expander(void (*expand_doit_func)(void *, Main *, void *))
|
||||
{
|
||||
expand_doit = expand_doit_func;
|
||||
@@ -8913,6 +8939,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
|
||||
case ID_LS:
|
||||
expand_linestyle(fd, mainvar, (FreestyleLineStyle *)id);
|
||||
break;
|
||||
case ID_GD:
|
||||
expand_gpencil(fd, mainvar, (bGPdata *)id);
|
||||
break;
|
||||
}
|
||||
|
||||
do_it = true;
|
||||
|
@@ -2465,6 +2465,8 @@ static void write_gpencils(WriteData *wd, ListBase *lb)
|
||||
/* write gpd data block to file */
|
||||
writestruct(wd, ID_GD, "bGPdata", 1, gpd);
|
||||
|
||||
if (gpd->adt) write_animdata(wd, gpd->adt);
|
||||
|
||||
/* write grease-pencil layers to file */
|
||||
writelist(wd, DATA, "bGPDlayer", &gpd->layers);
|
||||
for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
|
||||
|
@@ -2365,6 +2365,83 @@ static bAnimChannelType ACF_DSSPK =
|
||||
acf_dsspk_setting_ptr /* pointer for setting */
|
||||
};
|
||||
|
||||
/* GPencil Expander ------------------------------------------- */
|
||||
|
||||
// TODO: just get this from RNA?
|
||||
static int acf_dsgpencil_icon(bAnimListElem *UNUSED(ale))
|
||||
{
|
||||
return ICON_GREASEPENCIL;
|
||||
}
|
||||
|
||||
/* get the appropriate flag(s) for the setting when it is valid */
|
||||
static int acf_dsgpencil_setting_flag(bAnimContext *UNUSED(ac), eAnimChannel_Settings setting, bool *neg)
|
||||
{
|
||||
/* clear extra return data first */
|
||||
*neg = false;
|
||||
|
||||
switch (setting) {
|
||||
case ACHANNEL_SETTING_EXPAND: /* expanded */
|
||||
return GP_DATA_EXPAND;
|
||||
|
||||
case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */
|
||||
return ADT_NLA_EVAL_OFF;
|
||||
|
||||
case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
|
||||
*neg = true;
|
||||
return ADT_CURVES_NOT_VISIBLE;
|
||||
|
||||
case ACHANNEL_SETTING_SELECT: /* selected */
|
||||
return ADT_UI_SELECTED;
|
||||
|
||||
default: /* unsupported */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* get pointer to the setting */
|
||||
static void *acf_dsgpencil_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type)
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)ale->data;
|
||||
|
||||
/* clear extra return data first */
|
||||
*type = 0;
|
||||
|
||||
switch (setting) {
|
||||
case ACHANNEL_SETTING_EXPAND: /* expanded */
|
||||
return GET_ACF_FLAG_PTR(gpd->flag, type);
|
||||
|
||||
case ACHANNEL_SETTING_SELECT: /* selected */
|
||||
case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
|
||||
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
|
||||
if (gpd->adt)
|
||||
return GET_ACF_FLAG_PTR(gpd->adt->flag, type);
|
||||
return NULL;
|
||||
|
||||
default: /* unsupported */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* grease pencil expander type define */
|
||||
static bAnimChannelType ACF_DSGPENCIL =
|
||||
{
|
||||
"GPencil DS Expander", /* type name */
|
||||
ACHANNEL_ROLE_EXPANDER, /* role */
|
||||
|
||||
acf_generic_dataexpand_color, /* backdrop color */
|
||||
acf_generic_dataexpand_backdrop, /* backdrop */
|
||||
acf_generic_indention_1, /* indent level */
|
||||
acf_generic_basic_offset, /* offset */
|
||||
|
||||
acf_generic_idblock_name, /* name */
|
||||
acf_generic_idblock_name_prop, /* name prop */
|
||||
acf_dsgpencil_icon, /* icon */
|
||||
|
||||
acf_generic_dataexpand_setting_valid, /* has setting */
|
||||
acf_dsgpencil_setting_flag, /* flag for setting */
|
||||
acf_dsgpencil_setting_ptr /* pointer for setting */
|
||||
};
|
||||
|
||||
/* ShapeKey Entry ------------------------------------------- */
|
||||
|
||||
/* name for ShapeKey */
|
||||
@@ -3162,6 +3239,7 @@ static void ANIM_init_channel_typeinfo_data(void)
|
||||
animchannelTypeInfo[type++] = &ACF_DSLAT; /* Lattice Channel */
|
||||
animchannelTypeInfo[type++] = &ACF_DSLINESTYLE; /* LineStyle Channel */
|
||||
animchannelTypeInfo[type++] = &ACF_DSSPK; /* Speaker Channel */
|
||||
animchannelTypeInfo[type++] = &ACF_DSGPENCIL; /* GreasePencil Channel */
|
||||
|
||||
animchannelTypeInfo[type++] = &ACF_SHAPEKEY; /* ShapeKey */
|
||||
|
||||
@@ -3407,6 +3485,7 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float
|
||||
/* step 4) draw special toggles .................................
|
||||
* - in Graph Editor, checkboxes for visibility in curves area
|
||||
* - in NLA Editor, glowing dots for solo/not solo...
|
||||
* - in Grease Pencil mode, color swatches for layer color
|
||||
*/
|
||||
if (ac->sl) {
|
||||
if ((ac->spacetype == SPACE_IPO) && acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) {
|
||||
@@ -3431,6 +3510,10 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float
|
||||
/* just skip - drawn as widget now */
|
||||
offset += ICON_WIDTH;
|
||||
}
|
||||
else if (ale->type == ANIMTYPE_GPLAYER) {
|
||||
/* just skip - drawn as a widget */
|
||||
offset += ICON_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
/* step 5) draw name ............................................... */
|
||||
@@ -3866,6 +3949,7 @@ void ANIM_channel_draw_widgets(bContext *C, bAnimContext *ac, bAnimListElem *ale
|
||||
/* step 3) draw special toggles .................................
|
||||
* - in Graph Editor, checkboxes for visibility in curves area
|
||||
* - in NLA Editor, glowing dots for solo/not solo...
|
||||
* - in Grease Pencil mode, color swatches for layer color
|
||||
*/
|
||||
if (ac->sl) {
|
||||
if ((ac->spacetype == SPACE_IPO) && acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) {
|
||||
@@ -3878,6 +3962,23 @@ void ANIM_channel_draw_widgets(bContext *C, bAnimContext *ac, bAnimListElem *ale
|
||||
draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_SOLO);
|
||||
offset += ICON_WIDTH;
|
||||
}
|
||||
else if (ale->type == ANIMTYPE_GPLAYER) {
|
||||
/* color swatch for layer color */
|
||||
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
||||
PointerRNA ptr;
|
||||
|
||||
RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr);
|
||||
|
||||
UI_block_emboss_set(block, UI_EMBOSS);
|
||||
|
||||
uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset, yminc, ICON_WIDTH, ICON_WIDTH,
|
||||
&ptr, "color", -1,
|
||||
0, 0, 0, 0, gpl->info);
|
||||
|
||||
UI_block_emboss_set(block, UI_EMBOSS_NONE);
|
||||
|
||||
offset += ICON_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
/* step 4) draw text - check if renaming widget is in use... */
|
||||
|
@@ -129,6 +129,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
|
||||
case ANIMTYPE_DSLAT:
|
||||
case ANIMTYPE_DSLINESTYLE:
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
/* need to verify that this data is valid for now */
|
||||
if (ale->adt) {
|
||||
@@ -136,6 +137,13 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ANIMTYPE_GPLAYER:
|
||||
{
|
||||
bGPDlayer *gpl = (bGPDlayer *)ale->data;
|
||||
|
||||
ACHANNEL_SET_FLAG(gpl, ACHANNEL_SETFLAG_CLEAR, GP_LAYER_ACTIVE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +184,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSNTREE:
|
||||
case ANIMTYPE_DSTEX:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
/* need to verify that this data is valid for now */
|
||||
if (ale && ale->adt) {
|
||||
@@ -184,8 +193,14 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
|
||||
break;
|
||||
}
|
||||
|
||||
/* unhandled currently, but may be interesting */
|
||||
case ANIMTYPE_GPLAYER:
|
||||
{
|
||||
bGPDlayer *gpl = (bGPDlayer *)channel_data;
|
||||
gpl->flag |= GP_LAYER_ACTIVE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* unhandled currently, but may be interesting */
|
||||
case ANIMTYPE_MASKLAYER:
|
||||
case ANIMTYPE_SHAPEKEY:
|
||||
case ANIMTYPE_NLAACTION:
|
||||
@@ -268,6 +283,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
|
||||
case ANIMTYPE_DSLAT:
|
||||
case ANIMTYPE_DSLINESTYLE:
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
|
||||
sel = ACHANNEL_SETFLAG_CLEAR;
|
||||
@@ -361,6 +377,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
|
||||
case ANIMTYPE_DSLAT:
|
||||
case ANIMTYPE_DSLINESTYLE:
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
/* need to verify that this data is valid for now */
|
||||
if (ale->adt) {
|
||||
@@ -843,6 +860,13 @@ static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *sr
|
||||
is_sel = SEL_NLT(nlt);
|
||||
break;
|
||||
}
|
||||
case ANIMTYPE_GPLAYER:
|
||||
{
|
||||
bGPDlayer *gpl = (bGPDlayer *)channel;
|
||||
|
||||
is_sel = SEL_GPL(gpl);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type);
|
||||
return;
|
||||
@@ -1167,6 +1191,47 @@ static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrange
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode)
|
||||
{
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
|
||||
/* get rearranging function */
|
||||
AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
|
||||
|
||||
if (rearrange_func == NULL)
|
||||
return;
|
||||
|
||||
/* get Grease Pencil datablocks */
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
|
||||
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
ListBase anim_data_visible = {NULL, NULL};
|
||||
bGPdata *gpd = ale->data;
|
||||
|
||||
/* only consider layers if this datablock is open */
|
||||
BLI_assert(ale->type == ANIMTYPE_GPDATABLOCK);
|
||||
if ((gpd->flag & GP_DATA_EXPAND) == 0)
|
||||
continue;
|
||||
|
||||
/* Filter visible data. */
|
||||
rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GPLAYER);
|
||||
|
||||
/* rearrange datablock's layers */
|
||||
rearrange_animchannel_islands(&gpd->layers, rearrange_func, mode, ANIMTYPE_GPLAYER, &anim_data_visible);
|
||||
|
||||
/* free visible layers data */
|
||||
BLI_freelistN(&anim_data_visible);
|
||||
}
|
||||
|
||||
/* free GPD channel data */
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bAnimContext ac;
|
||||
@@ -1182,7 +1247,7 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
|
||||
/* method to move channels depends on the editor */
|
||||
if (ac.datatype == ANIMCONT_GPENCIL) {
|
||||
/* Grease Pencil channels */
|
||||
printf("Grease Pencil not supported for moving yet\n");
|
||||
rearrange_gpencil_channels(&ac, mode);
|
||||
}
|
||||
else if (ac.datatype == ANIMCONT_MASK) {
|
||||
/* Grease Pencil channels */
|
||||
@@ -2567,6 +2632,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
|
||||
case ANIMTYPE_DSLAT:
|
||||
case ANIMTYPE_DSLINESTYLE:
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
/* sanity checking... */
|
||||
if (ale->adt) {
|
||||
@@ -2728,7 +2794,13 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
|
||||
gpl->flag |= GP_LAYER_SELECT;
|
||||
}
|
||||
|
||||
notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
|
||||
/* change active layer, if this is selected (since we must always have an active layer) */
|
||||
if (gpl->flag & GP_LAYER_SELECT) {
|
||||
ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); /* Grease Pencil updates */
|
||||
notifierFlags |= (ND_ANIMCHAN | NA_EDITED); /* Animation Ediotrs updates */
|
||||
break;
|
||||
}
|
||||
case ANIMTYPE_MASKDATABLOCK:
|
||||
|
@@ -771,6 +771,21 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne
|
||||
ale->adt = BKE_animdata_from_id(data);
|
||||
break;
|
||||
}
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)data;
|
||||
AnimData *adt = gpd->adt;
|
||||
|
||||
/* NOTE: we just reuse the same expand filter for this case */
|
||||
ale->flag = EXPANDED_GPD(gpd);
|
||||
|
||||
// XXX: currently, this is only used for access to its animation data
|
||||
ale->key_data = (adt) ? adt->action : NULL;
|
||||
ale->datatype = ALE_ACT;
|
||||
|
||||
ale->adt = BKE_animdata_from_id(data);
|
||||
break;
|
||||
}
|
||||
case ANIMTYPE_GROUP:
|
||||
{
|
||||
bActionGroup *agrp = (bActionGroup *)data;
|
||||
@@ -1413,27 +1428,77 @@ static size_t animdata_filter_gpencil(ListBase *anim_data, void *UNUSED(data), i
|
||||
/* only show if gpd is used by something... */
|
||||
if (ID_REAL_USERS(gpd) < 1)
|
||||
continue;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* now add the list of collected channels */
|
||||
BLI_movelisttolist(anim_data, &tmp_data);
|
||||
BLI_assert(BLI_listbase_is_empty(&tmp_data));
|
||||
items += tmp_items;
|
||||
/* 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 {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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 the number of items added to the list */
|
||||
return items;
|
||||
}
|
||||
|
||||
/* Helper for Grease Pencil data integrated with main DopeSheet */
|
||||
static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, bGPdata *gpd, int filter_mode)
|
||||
{
|
||||
ListBase tmp_data = {NULL, NULL};
|
||||
size_t tmp_items = 0;
|
||||
size_t items = 0;
|
||||
|
||||
/* add relevant animation channels for Grease Pencil */
|
||||
BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_GPD(gpd))
|
||||
{
|
||||
/* add animation channels */
|
||||
tmp_items += animfilter_block_data(ac, &tmp_data, ads, &gpd->id, filter_mode);
|
||||
|
||||
/* add Grease Pencil layers */
|
||||
// TODO: do these need a separate expander?
|
||||
// XXX: what order should these go in?
|
||||
}
|
||||
END_ANIMFILTER_SUBCHANNELS;
|
||||
|
||||
/* did we find anything? */
|
||||
if (tmp_items) {
|
||||
/* include data-expand widget first */
|
||||
if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
|
||||
/* check if filtering by active status */
|
||||
// XXX: active check here needs checking
|
||||
if (ANIMCHANNEL_ACTIVEOK(gpd)) {
|
||||
ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_DSGPENCIL, gpd);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 the number of items added to the list */
|
||||
@@ -2225,6 +2290,11 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data
|
||||
if ((ob->particlesystem.first) && !(ads->filterflag & ADS_FILTER_NOPART)) {
|
||||
tmp_items += animdata_filter_ds_particles(ac, &tmp_data, ads, ob, filter_mode);
|
||||
}
|
||||
|
||||
/* grease pencil */
|
||||
if ((ob->gpd) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) {
|
||||
tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, ob->gpd, filter_mode);
|
||||
}
|
||||
}
|
||||
END_ANIMFILTER_SUBCHANNELS;
|
||||
|
||||
@@ -2359,6 +2429,7 @@ static size_t animdata_filter_dopesheet_scene(bAnimContext *ac, ListBase *anim_d
|
||||
BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_SCEC(sce))
|
||||
{
|
||||
bNodeTree *ntree = sce->nodetree;
|
||||
bGPdata *gpd = sce->gpd;
|
||||
World *wo = sce->world;
|
||||
|
||||
/* Action, Drivers, or NLA for Scene */
|
||||
@@ -2381,6 +2452,11 @@ static size_t animdata_filter_dopesheet_scene(bAnimContext *ac, ListBase *anim_d
|
||||
tmp_items += animdata_filter_ds_linestyle(ac, &tmp_data, ads, sce, filter_mode);
|
||||
}
|
||||
|
||||
/* grease pencil */
|
||||
if ((gpd) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) {
|
||||
tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, gpd, filter_mode);
|
||||
}
|
||||
|
||||
/* TODO: one day, when sequencer becomes its own datatype, perhaps it should be included here */
|
||||
}
|
||||
END_ANIMFILTER_SUBCHANNELS;
|
||||
|
@@ -154,6 +154,7 @@ static DLRBT_Node *nalloc_ak_gpframe(void *data)
|
||||
/* store settings based on state of BezTriple */
|
||||
ak->cfra = gpf->framenum;
|
||||
ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0;
|
||||
ak->key_type = gpf->key_type;
|
||||
|
||||
/* set 'modified', since this is used to identify long keyframes */
|
||||
ak->modified = 1;
|
||||
@@ -170,6 +171,10 @@ static void nupdate_ak_gpframe(void *node, void *data)
|
||||
/* set selection status and 'touched' status */
|
||||
if (gpf->flag & GP_FRAME_SELECT) ak->sel = SELECT;
|
||||
ak->modified += 1;
|
||||
|
||||
/* for keyframe type, 'proper' keyframes have priority over breakdowns (and other types for now) */
|
||||
if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME)
|
||||
ak->key_type = BEZT_KEYTYPE_KEYFRAME;
|
||||
}
|
||||
|
||||
/* ......... */
|
||||
@@ -731,6 +736,21 @@ void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos)
|
||||
BLI_dlrbTree_free(&blocks);
|
||||
}
|
||||
|
||||
void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos)
|
||||
{
|
||||
DLRBT_Tree keys;
|
||||
|
||||
BLI_dlrbTree_init(&keys);
|
||||
|
||||
gpencil_to_keylist(ads, gpd, &keys);
|
||||
|
||||
BLI_dlrbTree_linkedlist_sync(&keys);
|
||||
|
||||
draw_keylist(v2d, &keys, NULL, ypos, 0);
|
||||
|
||||
BLI_dlrbTree_free(&keys);
|
||||
}
|
||||
|
||||
void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos)
|
||||
{
|
||||
DLRBT_Tree keys;
|
||||
@@ -923,6 +943,20 @@ void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, DLRBT_Tree
|
||||
}
|
||||
|
||||
|
||||
void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys)
|
||||
{
|
||||
bGPDlayer *gpl;
|
||||
|
||||
if (gpd && keys) {
|
||||
/* for now, just aggregate out all the frames, but only for visible layers */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
if ((gpl->flag & GP_LAYER_HIDE) == 0) {
|
||||
gpl_to_keylist(ads, gpl, keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys)
|
||||
{
|
||||
bGPDframe *gpf;
|
||||
|
@@ -43,7 +43,9 @@ set(SRC
|
||||
gpencil_edit.c
|
||||
gpencil_ops.c
|
||||
gpencil_paint.c
|
||||
gpencil_select.c
|
||||
gpencil_undo.c
|
||||
gpencil_utils.c
|
||||
|
||||
gpencil_intern.h
|
||||
)
|
||||
|
@@ -58,6 +58,10 @@
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "gpencil_intern.h"
|
||||
|
||||
/* ************************************************** */
|
||||
/* GREASE PENCIL DRAWING */
|
||||
|
||||
@@ -71,6 +75,9 @@ typedef enum eDrawStrokeFlags {
|
||||
GP_DRAWDATA_ONLYI2D = (1 << 3), /* only draw 'image' strokes */
|
||||
GP_DRAWDATA_IEDITHACK = (1 << 4), /* special hack for drawing strokes in Image Editor (weird coordinates) */
|
||||
GP_DRAWDATA_NO_XRAY = (1 << 5), /* don't draw xray in 3D view (which is default) */
|
||||
GP_DRAWDATA_NO_ONIONS = (1 << 6), /* no onionskins should be drawn (for animation playback) */
|
||||
GP_DRAWDATA_VOLUMETRIC = (1 << 7), /* draw strokes as "volumetric" circular billboards */
|
||||
GP_DRAWDATA_FILL = (1 << 8), /* fill insides/bounded-regions of strokes */
|
||||
} eDrawStrokeFlags;
|
||||
|
||||
|
||||
@@ -142,6 +149,200 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn
|
||||
}
|
||||
}
|
||||
|
||||
/* --------- 2D Stroke Drawing Helpers --------- */
|
||||
|
||||
/* helper function to calculate x-y drawing coordinates for 2D points */
|
||||
static void gp_calc_2d_stroke_xy(bGPDspoint *pt, short sflag, int offsx, int offsy, int winx, int winy, float r_co[2])
|
||||
{
|
||||
if (sflag & GP_STROKE_2DSPACE) {
|
||||
r_co[0] = pt->x;
|
||||
r_co[1] = pt->y;
|
||||
}
|
||||
else if (sflag & GP_STROKE_2DIMAGE) {
|
||||
const float x = (float)((pt->x * winx) + offsx);
|
||||
const float y = (float)((pt->y * winy) + offsy);
|
||||
|
||||
r_co[0] = x;
|
||||
r_co[1] = y;
|
||||
}
|
||||
else {
|
||||
const float x = (float)(pt->x / 100 * winx) + offsx;
|
||||
const float y = (float)(pt->y / 100 * winy) + offsy;
|
||||
|
||||
r_co[0] = x;
|
||||
r_co[1] = y;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------- Volumetric Strokes --------------- */
|
||||
|
||||
/* draw a 2D buffer stroke in "volumetric" style
|
||||
* NOTE: the stroke buffer doesn't have any coordinate offsets/transforms
|
||||
*/
|
||||
static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, short thickness, short dflag, short sflag)
|
||||
{
|
||||
GLUquadricObj *qobj = gluNewQuadric();
|
||||
float modelview[4][4];
|
||||
|
||||
tGPspoint *pt;
|
||||
int i;
|
||||
|
||||
/* error checking */
|
||||
if ((points == NULL) || (totpoints <= 0))
|
||||
return;
|
||||
|
||||
/* check if buffer can be drawn */
|
||||
if (dflag & (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_ONLYV2D))
|
||||
return;
|
||||
|
||||
/* get basic matrix - should be camera space (i.e "identity") */
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, (float *)modelview);
|
||||
|
||||
/* draw points */
|
||||
glPushMatrix();
|
||||
|
||||
for (i = 0, pt = points; i < totpoints; i++, pt++) {
|
||||
/* set the transformed position */
|
||||
// TODO: scale should change based on zoom level, which requires proper translation mult too!
|
||||
modelview[3][0] = pt->x;
|
||||
modelview[3][1] = pt->y;
|
||||
|
||||
glLoadMatrixf((float *)modelview);
|
||||
|
||||
/* draw the disk using the current state... */
|
||||
gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1);
|
||||
|
||||
|
||||
modelview[3][0] = modelview[3][1] = 0.0f;
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
gluDeleteQuadric(qobj);
|
||||
}
|
||||
|
||||
/* draw a 2D strokes in "volumetric" style */
|
||||
static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag,
|
||||
int offsx, int offsy, int winx, int winy)
|
||||
{
|
||||
GLUquadricObj *qobj = gluNewQuadric();
|
||||
float modelview[4][4];
|
||||
float baseloc[3];
|
||||
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
|
||||
/* get basic matrix */
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, (float *)modelview);
|
||||
copy_v3_v3(baseloc, modelview[3]);
|
||||
|
||||
/* draw points */
|
||||
glPushMatrix();
|
||||
|
||||
for (i = 0, pt = points; i < totpoints; i++, pt++) {
|
||||
/* set the transformed position */
|
||||
float co[2];
|
||||
|
||||
gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
|
||||
translate_m4(modelview, co[0], co[1], 0.0f);
|
||||
|
||||
glLoadMatrixf((float *)modelview);
|
||||
|
||||
/* draw the disk using the current state... */
|
||||
gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1);
|
||||
|
||||
/* restore matrix */
|
||||
copy_v3_v3(modelview[3], baseloc);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
gluDeleteQuadric(qobj);
|
||||
}
|
||||
|
||||
/* draw a 3D stroke in "volumetric" style */
|
||||
static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag)
|
||||
{
|
||||
GLUquadricObj *qobj = gluNewQuadric();
|
||||
|
||||
float base_modelview[4][4], modelview[4][4];
|
||||
float base_loc[3];
|
||||
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
|
||||
/* Get the basic modelview matrix we use for performing calculations */
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, (float *)base_modelview);
|
||||
copy_v3_v3(base_loc, base_modelview[3]);
|
||||
|
||||
/* Create the basic view-aligned billboard matrix we're going to actually draw qobj with:
|
||||
* - We need to knock out the rotation so that we are
|
||||
* simply left with a camera-facing billboard
|
||||
* - The scale factors here are chosen so that the thickness
|
||||
* is relatively reasonable. Otherwise, it gets far too
|
||||
* large!
|
||||
*/
|
||||
scale_m4_fl(modelview, 0.1f);
|
||||
|
||||
/* draw each point as a disk... */
|
||||
glPushMatrix();
|
||||
|
||||
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
|
||||
/* apply translation to base_modelview, so that the translated point is put in the right place */
|
||||
translate_m4(base_modelview, pt->x, pt->y, pt->z);
|
||||
|
||||
/* copy the translation component to the billboard matrix we're going to use,
|
||||
* then reset the base matrix to the original values so that we can do the same
|
||||
* for the next point without accumulation/pollution effects
|
||||
*/
|
||||
copy_v3_v3(modelview[3], base_modelview[3]); /* copy offset value */
|
||||
copy_v3_v3(base_modelview[3], base_loc); /* restore */
|
||||
|
||||
/* apply our billboard matrix for drawing... */
|
||||
glLoadMatrixf((float *)modelview);
|
||||
|
||||
/* draw the disk using the current state... */
|
||||
gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
gluDeleteQuadric(qobj);
|
||||
}
|
||||
|
||||
|
||||
/* --------------- Stroke Fills ----------------- */
|
||||
|
||||
/* draw fills for shapes */
|
||||
static void gp_draw_stroke_fill(bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag,
|
||||
int offsx, int offsy, int winx, int winy)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
BLI_assert(totpoints >= 3);
|
||||
|
||||
/* As an initial implementation, we use the OpenGL filled polygon drawing
|
||||
* here since it's the easiest option to implement for this case. It does
|
||||
* come with limitations (notably for concave shapes), though it shouldn't
|
||||
* be much of an issue in most cases.
|
||||
*/
|
||||
glBegin(GL_POLYGON);
|
||||
|
||||
for (i = 0, pt = points; i < totpoints; i++, pt++) {
|
||||
if (sflag & GP_STROKE_3DSPACE) {
|
||||
glVertex3fv(&pt->x);
|
||||
}
|
||||
else {
|
||||
float co[2];
|
||||
|
||||
gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
|
||||
glVertex2fv(co);
|
||||
}
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/* ----- Existing Strokes Drawing (3D and Point) ------ */
|
||||
|
||||
/* draw a given stroke - just a single dot (only one point) */
|
||||
@@ -158,18 +359,7 @@ static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dfla
|
||||
float co[2];
|
||||
|
||||
/* get coordinates of point */
|
||||
if (sflag & GP_STROKE_2DSPACE) {
|
||||
co[0] = points->x;
|
||||
co[1] = points->y;
|
||||
}
|
||||
else if (sflag & GP_STROKE_2DIMAGE) {
|
||||
co[0] = (points->x * winx) + offsx;
|
||||
co[1] = (points->y * winy) + offsy;
|
||||
}
|
||||
else {
|
||||
co[0] = (points->x / 100 * winx) + offsx;
|
||||
co[1] = (points->y / 100 * winy) + offsy;
|
||||
}
|
||||
gp_calc_2d_stroke_xy(points, sflag, offsx, offsy, winx, winy, co);
|
||||
|
||||
/* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok
|
||||
* - also mandatory in if Image Editor 'image-based' dot
|
||||
@@ -198,7 +388,7 @@ static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dfla
|
||||
}
|
||||
|
||||
/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */
|
||||
static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, short debug)
|
||||
static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, short sflag)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
float curpressure = points[0].pressure;
|
||||
@@ -231,6 +421,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
|
||||
glEnd();
|
||||
|
||||
/* draw debug points of curve on top? */
|
||||
/* XXX: for now, we represent "selected" strokes in the same way as debug, which isn't used anymore */
|
||||
if (debug) {
|
||||
glBegin(GL_POINTS);
|
||||
for (i = 0, pt = points; i < totpoints && pt; i++, pt++)
|
||||
@@ -242,8 +433,8 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
|
||||
/* ----- Fancy 2D-Stroke Drawing ------ */
|
||||
|
||||
/* draw a given stroke in 2d */
|
||||
static void gp_draw_stroke(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag,
|
||||
short debug, int offsx, int offsy, int winx, int winy)
|
||||
static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag,
|
||||
bool debug, int offsx, int offsy, int winx, int winy)
|
||||
{
|
||||
/* otherwise thickness is twice that of the 3D view */
|
||||
float thickness = (float)thickness_s * 0.5f;
|
||||
@@ -259,21 +450,10 @@ static void gp_draw_stroke(bGPDspoint *points, int totpoints, short thickness_s,
|
||||
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
|
||||
if (sflag & GP_STROKE_2DSPACE) {
|
||||
glVertex2f(pt->x, pt->y);
|
||||
}
|
||||
else if (sflag & GP_STROKE_2DIMAGE) {
|
||||
const float x = (pt->x * winx) + offsx;
|
||||
const float y = (pt->y * winy) + offsy;
|
||||
|
||||
glVertex2f(x, y);
|
||||
}
|
||||
else {
|
||||
const float x = (pt->x / 100 * winx) + offsx;
|
||||
const float y = (pt->y / 100 * winy) + offsy;
|
||||
|
||||
glVertex2f(x, y);
|
||||
}
|
||||
float co[2];
|
||||
|
||||
gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
|
||||
glVertex2fv(co);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
@@ -297,22 +477,8 @@ static void gp_draw_stroke(bGPDspoint *points, int totpoints, short thickness_s,
|
||||
float pthick; /* thickness at segment point */
|
||||
|
||||
/* get x and y coordinates from points */
|
||||
if (sflag & GP_STROKE_2DSPACE) {
|
||||
s0[0] = pt1->x; s0[1] = pt1->y;
|
||||
s1[0] = pt2->x; s1[1] = pt2->y;
|
||||
}
|
||||
else if (sflag & GP_STROKE_2DIMAGE) {
|
||||
s0[0] = (pt1->x * winx) + offsx;
|
||||
s0[1] = (pt1->y * winy) + offsy;
|
||||
s1[0] = (pt2->x * winx) + offsx;
|
||||
s1[1] = (pt2->y * winy) + offsy;
|
||||
}
|
||||
else {
|
||||
s0[0] = (pt1->x / 100 * winx) + offsx;
|
||||
s0[1] = (pt1->y / 100 * winy) + offsy;
|
||||
s1[0] = (pt2->x / 100 * winx) + offsx;
|
||||
s1[1] = (pt2->y / 100 * winy) + offsy;
|
||||
}
|
||||
gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0);
|
||||
gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1);
|
||||
|
||||
/* calculate gradient and normal - 'angle'=(ny/nx) */
|
||||
m1[1] = s1[1] - s0[1];
|
||||
@@ -446,52 +612,57 @@ static void gp_draw_stroke(bGPDspoint *points, int totpoints, short thickness_s,
|
||||
|
||||
glBegin(GL_POINTS);
|
||||
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
|
||||
if (sflag & GP_STROKE_2DSPACE) {
|
||||
glVertex2fv(&pt->x);
|
||||
}
|
||||
else if (sflag & GP_STROKE_2DIMAGE) {
|
||||
const float x = (float)((pt->x * winx) + offsx);
|
||||
const float y = (float)((pt->y * winy) + offsy);
|
||||
|
||||
glVertex2f(x, y);
|
||||
}
|
||||
else {
|
||||
const float x = (float)(pt->x / 100 * winx) + offsx;
|
||||
const float y = (float)(pt->y / 100 * winy) + offsy;
|
||||
|
||||
glVertex2f(x, y);
|
||||
}
|
||||
float co[2];
|
||||
|
||||
gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
|
||||
glVertex2fv(co);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- General Drawing ------ */
|
||||
/* ----- Strokes Drawing ------ */
|
||||
|
||||
/* Helper for doing all the checks on whether a stroke can be drawn */
|
||||
static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag)
|
||||
{
|
||||
/* skip stroke if it isn't in the right display space for this drawing context */
|
||||
/* 1) 3D Strokes */
|
||||
if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE))
|
||||
return false;
|
||||
if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE))
|
||||
return false;
|
||||
|
||||
/* 2) Screen Space 2D Strokes */
|
||||
if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE))
|
||||
return false;
|
||||
if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE))
|
||||
return false;
|
||||
|
||||
/* 3) Image Space (2D) */
|
||||
if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE))
|
||||
return false;
|
||||
if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE))
|
||||
return false;
|
||||
|
||||
|
||||
/* skip stroke if it doesn't have any valid data */
|
||||
if ((gps->points == NULL) || (gps->totpoints < 1))
|
||||
return false;
|
||||
|
||||
/* stroke can be drawn */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* draw a set of strokes */
|
||||
static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag,
|
||||
short debug, short lthick, const float color[4])
|
||||
bool debug, short lthick, const float color[4], const float fill_color[4])
|
||||
{
|
||||
bGPDstroke *gps;
|
||||
|
||||
/* set color first (may need to reset it again later too) */
|
||||
glColor4fv(color);
|
||||
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
/* check if stroke can be drawn - checks here generally fall into pairs */
|
||||
if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE))
|
||||
continue;
|
||||
if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE))
|
||||
continue;
|
||||
if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE))
|
||||
continue;
|
||||
if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE))
|
||||
continue;
|
||||
if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE))
|
||||
continue;
|
||||
if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE))
|
||||
continue;
|
||||
if ((gps->points == NULL) || (gps->totpoints < 1))
|
||||
/* check if stroke can be drawn */
|
||||
if (gp_can_draw_stroke(gps, dflag) == false)
|
||||
continue;
|
||||
|
||||
/* check which stroke-drawer to use */
|
||||
@@ -513,11 +684,27 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gps->totpoints == 1) {
|
||||
gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
|
||||
/* 3D Fill */
|
||||
if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
|
||||
glColor4fv(fill_color);
|
||||
gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
|
||||
}
|
||||
|
||||
/* 3D Stroke */
|
||||
glColor4fv(color);
|
||||
|
||||
if (dflag & GP_DRAWDATA_VOLUMETRIC) {
|
||||
/* volumetric stroke drawing */
|
||||
gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag);
|
||||
}
|
||||
else {
|
||||
gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug);
|
||||
/* 3D Lines - OpenGL primitives-based */
|
||||
if (gps->totpoints == 1) {
|
||||
gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
|
||||
}
|
||||
else {
|
||||
gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag);
|
||||
}
|
||||
}
|
||||
|
||||
if (no_xray) {
|
||||
@@ -532,16 +719,231 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gps->totpoints == 1) {
|
||||
gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
|
||||
/* 2D - Fill */
|
||||
if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
|
||||
gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
|
||||
}
|
||||
|
||||
/* 2D Strokes... */
|
||||
glColor4fv(color);
|
||||
|
||||
if (dflag & GP_DRAWDATA_VOLUMETRIC) {
|
||||
/* blob/disk-based "volumetric" drawing */
|
||||
gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
|
||||
}
|
||||
else {
|
||||
gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy);
|
||||
/* normal 2D strokes */
|
||||
if (gps->totpoints == 1) {
|
||||
gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
|
||||
}
|
||||
else {
|
||||
gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw selected verts for strokes being edited */
|
||||
static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, const float tcolor[3])
|
||||
{
|
||||
bGPDstroke *gps;
|
||||
|
||||
const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY);
|
||||
int mask_orig = 0;
|
||||
|
||||
/* set up depth masks... */
|
||||
if (dflag & GP_DRAWDATA_ONLY3D) {
|
||||
if (no_xray) {
|
||||
glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
|
||||
glDepthMask(0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
/* first arg is normally rv3d->dist, but this isn't
|
||||
* available here and seems to work quite well without */
|
||||
bglPolygonOffset(1.0f, 1.0f);
|
||||
#if 0
|
||||
glEnable(GL_POLYGON_OFFSET_LINE);
|
||||
glPolygonOffset(-1.0f, -1.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* draw stroke verts */
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
bGPDspoint *pt;
|
||||
float vsize, bsize;
|
||||
int i;
|
||||
|
||||
/* check if stroke can be drawn */
|
||||
if (gp_can_draw_stroke(gps, dflag) == false)
|
||||
continue;
|
||||
|
||||
/* Optimisation: only draw points for selected strokes
|
||||
* We assume that selected points can only occur in
|
||||
* strokes that are selected too.
|
||||
*/
|
||||
if ((gps->flag & GP_STROKE_SELECT) == 0)
|
||||
continue;
|
||||
|
||||
/* Get size of verts:
|
||||
* - The selected state needs to be larger than the unselected state so that
|
||||
* they stand out more.
|
||||
* - We use the theme setting for size of the unselected verts
|
||||
*/
|
||||
bsize = UI_GetThemeValuef(TH_VERTEX_SIZE);
|
||||
if ((int)bsize > 8) {
|
||||
vsize = 10.0f;
|
||||
bsize = 8.0f;
|
||||
}
|
||||
else {
|
||||
vsize = bsize + 2;
|
||||
}
|
||||
|
||||
/* First Pass: Draw all the verts (i.e. these become the unselected state) */
|
||||
if (tcolor != NULL) {
|
||||
/* for now, we assume that the base color of the points is not too close to the real color */
|
||||
glColor3fv(tcolor);
|
||||
}
|
||||
else {
|
||||
/* this doesn't work well with the default theme and black strokes... */
|
||||
UI_ThemeColor(TH_VERTEX);
|
||||
}
|
||||
glPointSize(bsize);
|
||||
|
||||
glBegin(GL_POINTS);
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
|
||||
if (gps->flag & GP_STROKE_3DSPACE) {
|
||||
glVertex3fv(&pt->x);
|
||||
}
|
||||
else {
|
||||
float co[2];
|
||||
|
||||
gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
|
||||
glVertex2fv(co);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
|
||||
/* Second Pass: Draw only verts which are selected */
|
||||
UI_ThemeColor(TH_VERTEX_SELECT);
|
||||
glPointSize(vsize);
|
||||
|
||||
glBegin(GL_POINTS);
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
if (gps->flag & GP_STROKE_3DSPACE) {
|
||||
glVertex3fv(&pt->x);
|
||||
}
|
||||
else {
|
||||
float co[2];
|
||||
|
||||
gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
|
||||
glVertex2fv(co);
|
||||
}
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/* clear depth mask */
|
||||
if (dflag & GP_DRAWDATA_ONLY3D) {
|
||||
if (no_xray) {
|
||||
glDepthMask(mask_orig);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
bglPolygonOffset(0.0, 0.0);
|
||||
#if 0
|
||||
glDisable(GL_POLYGON_OFFSET_LINE);
|
||||
glPolygonOffset(0, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- General Drawing ------ */
|
||||
|
||||
/* draw onion-skinning for a layer */
|
||||
static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy,
|
||||
int cfra, int dflag, short debug, short lthick)
|
||||
{
|
||||
const float alpha = gpl->color[3];
|
||||
float color[4];
|
||||
|
||||
/* 1) Draw Previous Frames First */
|
||||
if (gpl->flag & GP_LAYER_GHOST_PREVCOL) {
|
||||
copy_v3_v3(color, gpl->gcolor_prev);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(color, gpl->color);
|
||||
}
|
||||
|
||||
if (gpl->gstep) {
|
||||
bGPDframe *gf;
|
||||
float fac;
|
||||
|
||||
/* draw previous frames first */
|
||||
for (gf = gpf->prev; gf; gf = gf->prev) {
|
||||
/* check if frame is drawable */
|
||||
if ((gpf->framenum - gf->framenum) <= gpl->gstep) {
|
||||
/* alpha decreases with distance from curframe index */
|
||||
fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1));
|
||||
color[3] = alpha * fac * 0.66f;
|
||||
gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* draw the strokes for the ghost frames (at half of the alpha set by user) */
|
||||
if (gpf->prev) {
|
||||
color[3] = (alpha / 7);
|
||||
gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 2) Now draw next frames */
|
||||
if (gpl->flag & GP_LAYER_GHOST_NEXTCOL) {
|
||||
copy_v3_v3(color, gpl->gcolor_next);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(color, gpl->color);
|
||||
}
|
||||
|
||||
if (gpl->gstep_next) {
|
||||
bGPDframe *gf;
|
||||
float fac;
|
||||
|
||||
/* now draw next frames */
|
||||
for (gf = gpf->next; gf; gf = gf->next) {
|
||||
/* check if frame is drawable */
|
||||
if ((gf->framenum - gpf->framenum) <= gpl->gstep_next) {
|
||||
/* alpha decreases with distance from curframe index */
|
||||
fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1));
|
||||
color[3] = alpha * fac * 0.66f;
|
||||
gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* draw the strokes for the ghost frames (at half of the alpha set by user) */
|
||||
if (gpf->next) {
|
||||
color[3] = (alpha / 4);
|
||||
gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
|
||||
}
|
||||
}
|
||||
|
||||
/* 3) restore alpha */
|
||||
glColor4fv(gpl->color);
|
||||
}
|
||||
|
||||
/* draw grease-pencil datablock */
|
||||
static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag)
|
||||
{
|
||||
@@ -561,9 +963,8 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy,
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
bGPDframe *gpf;
|
||||
|
||||
short debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? 1 : 0;
|
||||
bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false;
|
||||
short lthick = gpl->thickness;
|
||||
float color[4], tcolor[4];
|
||||
|
||||
/* don't draw layer if hidden */
|
||||
if (gpl->flag & GP_LAYER_HIDE)
|
||||
@@ -576,72 +977,54 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy,
|
||||
|
||||
/* set color, stroke thickness, and point size */
|
||||
glLineWidth(lthick);
|
||||
copy_v4_v4(color, gpl->color); // just for copying 4 array elements
|
||||
copy_v4_v4(tcolor, gpl->color); // additional copy of color (for ghosting)
|
||||
glColor4fv(color);
|
||||
glPointSize((float)(gpl->thickness + 2));
|
||||
|
||||
/* apply xray layer setting */
|
||||
if (gpl->flag & GP_LAYER_NO_XRAY) dflag |= GP_DRAWDATA_NO_XRAY;
|
||||
else dflag &= ~GP_DRAWDATA_NO_XRAY;
|
||||
/* Add layer drawing settings to the set of "draw flags"
|
||||
* NOTE: If the setting doesn't apply, it *must* be cleared,
|
||||
* as dflag's carry over from the previous layer
|
||||
*/
|
||||
#define GP_DRAWFLAG_APPLY(condition, draw_flag_value) { \
|
||||
if (condition) dflag |= (draw_flag_value); \
|
||||
else dflag &= ~(draw_flag_value); \
|
||||
} (void)0
|
||||
|
||||
/* xray... */
|
||||
GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_NO_XRAY), GP_DRAWDATA_NO_XRAY);
|
||||
|
||||
/* volumetric strokes... */
|
||||
GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC);
|
||||
|
||||
/* fill strokes... */
|
||||
// XXX: this is not a very good limit
|
||||
GP_DRAWFLAG_APPLY((gpl->fill[3] > 0.001f), GP_DRAWDATA_FILL);
|
||||
#undef GP_DRAWFLAG_APPLY
|
||||
|
||||
/* draw 'onionskins' (frame left + right) */
|
||||
if (gpl->flag & GP_LAYER_ONIONSKIN) {
|
||||
/* drawing method - only immediately surrounding (gstep = 0),
|
||||
* or within a frame range on either side (gstep > 0)*/
|
||||
if (gpl->gstep) {
|
||||
bGPDframe *gf;
|
||||
float fac;
|
||||
|
||||
/* draw previous frames first */
|
||||
for (gf = gpf->prev; gf; gf = gf->prev) {
|
||||
/* check if frame is drawable */
|
||||
if ((gpf->framenum - gf->framenum) <= gpl->gstep) {
|
||||
/* alpha decreases with distance from curframe index */
|
||||
fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1));
|
||||
tcolor[3] = color[3] * fac * 0.66f;
|
||||
gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* now draw next frames */
|
||||
for (gf = gpf->next; gf; gf = gf->next) {
|
||||
/* check if frame is drawable */
|
||||
if ((gf->framenum - gpf->framenum) <= gpl->gstep) {
|
||||
/* alpha decreases with distance from curframe index */
|
||||
fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep + 1));
|
||||
tcolor[3] = color[3] * fac * 0.66f;
|
||||
gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* restore alpha */
|
||||
glColor4fv(color);
|
||||
}
|
||||
else {
|
||||
/* draw the strokes for the ghost frames (at half of the alpha set by user) */
|
||||
if (gpf->prev) {
|
||||
tcolor[3] = (color[3] / 7);
|
||||
gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
|
||||
}
|
||||
|
||||
if (gpf->next) {
|
||||
tcolor[3] = (color[3] / 4);
|
||||
gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
|
||||
}
|
||||
|
||||
/* restore alpha */
|
||||
glColor4fv(color);
|
||||
}
|
||||
if ((gpl->flag & GP_LAYER_ONIONSKIN) && !(dflag & GP_DRAWDATA_NO_ONIONS)) {
|
||||
/* Drawing method - only immediately surrounding (gstep = 0),
|
||||
* or within a frame range on either side (gstep > 0)
|
||||
*/
|
||||
gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick);
|
||||
}
|
||||
|
||||
/* draw the strokes already in active frame */
|
||||
tcolor[3] = color[3];
|
||||
gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
|
||||
gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill);
|
||||
|
||||
/* Draw verts of selected strokes
|
||||
* - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering
|
||||
* - locked layers can't be edited, so there's no point showing these verts
|
||||
* as they will have no bearings on what gets edited
|
||||
* - only show when in editmode, since operators shouldn't work otherwise
|
||||
* (NOTE: doing it this way means that the toggling editmode shows visible change immediately)
|
||||
*/
|
||||
/* XXX: perhaps we don't want to show these when users are drawing... */
|
||||
if ((G.f & G_RENDER_OGL) == 0 &&
|
||||
(gpl->flag & GP_LAYER_LOCKED) == 0 &&
|
||||
(gpd->flag & GP_DATA_STROKE_EDITMODE))
|
||||
{
|
||||
gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag,
|
||||
(gpl->color[3] < 0.95f) ? gpl->color : NULL);
|
||||
}
|
||||
|
||||
/* Check if may need to draw the active stroke cache, only if this layer is the active layer
|
||||
* that is being edited. (Stroke buffer is currently stored in gp-data)
|
||||
@@ -649,9 +1032,21 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy,
|
||||
if (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) &&
|
||||
(gpf->flag & GP_FRAME_PAINT))
|
||||
{
|
||||
/* Set color for drawing buffer stroke - since this may not be set yet */
|
||||
glColor4fv(gpl->color);
|
||||
|
||||
/* Buffer stroke needs to be drawn with a different linestyle
|
||||
* to help differentiate them from normal strokes. */
|
||||
gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
|
||||
* to help differentiate them from normal strokes.
|
||||
*
|
||||
* It should also be noted that sbuffer contains temporary point types
|
||||
* i.e. tGPspoints NOT bGPDspoints
|
||||
*/
|
||||
if (gpl->flag & GP_LAYER_VOLUMETRIC) {
|
||||
gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
|
||||
}
|
||||
else {
|
||||
gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -251,6 +251,23 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl)
|
||||
}
|
||||
}
|
||||
|
||||
/* Set keyframe type for selected frames from given gp-layer
|
||||
* \param type The type of keyframe (eBezTriple_KeyframeType) to set selected frames to
|
||||
*/
|
||||
void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type)
|
||||
{
|
||||
bGPDframe *gpf;
|
||||
|
||||
if (gpl == NULL)
|
||||
return;
|
||||
|
||||
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||
if (gpf->flag & GP_FRAME_SELECT) {
|
||||
gpf->key_type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // XXX disabled until grease pencil code stabilises again
|
||||
/* -------------------------------------- */
|
||||
/* Copy and Paste Tools */
|
||||
|
@@ -143,13 +143,12 @@ static void gp_drawui_layer(uiLayout *layout, bGPdata *gpd, bGPDlayer *gpl, cons
|
||||
/* active */
|
||||
block = uiLayoutGetBlock(sub);
|
||||
icon = (gpl->flag & GP_LAYER_ACTIVE) ? ICON_RADIOBUT_ON : ICON_RADIOBUT_OFF;
|
||||
but = uiDefIconButBitI(block, UI_BTYPE_TOGGLE, GP_LAYER_ACTIVE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y,
|
||||
but = uiDefIconButBitS(block, UI_BTYPE_TOGGLE, GP_LAYER_ACTIVE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y,
|
||||
&gpl->flag, 0.0, 0.0, 0.0, 0.0, TIP_("Set active layer"));
|
||||
UI_but_func_set(but, gp_ui_activelayer_cb, gpd, gpl);
|
||||
|
||||
/* locked */
|
||||
icon = (gpl->flag & GP_LAYER_LOCKED) ? ICON_LOCKED : ICON_UNLOCKED;
|
||||
uiItemR(sub, &ptr, "lock", 0, "", icon);
|
||||
uiItemR(sub, &ptr, "lock", 0, "", ICON_NONE);
|
||||
|
||||
/* when layer is locked or hidden, only draw header */
|
||||
if (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE)) {
|
||||
@@ -157,7 +156,7 @@ static void gp_drawui_layer(uiLayout *layout, bGPdata *gpd, bGPDlayer *gpl, cons
|
||||
|
||||
/* visibility button (only if hidden but not locked!) */
|
||||
if ((gpl->flag & GP_LAYER_HIDE) && !(gpl->flag & GP_LAYER_LOCKED))
|
||||
uiItemR(sub, &ptr, "hide", 0, "", ICON_RESTRICT_VIEW_ON);
|
||||
uiItemR(sub, &ptr, "hide", 0, "", ICON_NONE);
|
||||
|
||||
/* name */
|
||||
if (gpl->flag & GP_LAYER_HIDE)
|
||||
@@ -182,7 +181,7 @@ static void gp_drawui_layer(uiLayout *layout, bGPdata *gpd, bGPDlayer *gpl, cons
|
||||
else {
|
||||
/* draw rest of header -------------------------------- */
|
||||
/* visibility button */
|
||||
uiItemR(sub, &ptr, "hide", 0, "", ICON_RESTRICT_VIEW_OFF);
|
||||
uiItemR(sub, &ptr, "hide", 0, "", ICON_NONE);
|
||||
|
||||
/* frame locking */
|
||||
/* TODO: this needs its own icons... */
|
||||
|
@@ -86,35 +86,51 @@
|
||||
/* ************************************************ */
|
||||
/* Context Wrangling... */
|
||||
|
||||
/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
|
||||
bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
|
||||
/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it,
|
||||
* when context info is not available.
|
||||
*/
|
||||
bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr)
|
||||
{
|
||||
ID *screen_id = (ID *)CTX_wm_screen(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
|
||||
/* if there's an active area, check if the particular editor may
|
||||
* have defined any special Grease Pencil context for editing...
|
||||
*/
|
||||
if (sa) {
|
||||
SpaceLink *sl = sa->spacedata.first;
|
||||
|
||||
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 */
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
||||
/* TODO: we can include other data-types such as bones later if need be... */
|
||||
|
||||
/* just in case no active/selected object */
|
||||
if (ob && (ob->flag & SELECT)) {
|
||||
/* for now, as long as there's an object, default to using that in 3D-View */
|
||||
if (ptr) RNA_id_pointer_create(&ob->id, ptr);
|
||||
/* default to using scene's data, unless it doesn't exist (and object's does instead) */
|
||||
/* XXX: this will require a toggle switch later to be more predictable */
|
||||
bool scene_ok = (scene != NULL);
|
||||
bool ob_ok = ((ob) && (ob->flag & SELECT) && (ob->gpd));
|
||||
|
||||
if (ob_ok || !scene_ok) {
|
||||
/* Object Case (not good for users):
|
||||
* - For existing files with object-level already,
|
||||
* or where user has explicitly assigned to object,
|
||||
* we can use the object as the host...
|
||||
*
|
||||
* - If there is no scene data provided (rare/impossible)
|
||||
* we will also be forced to use the object
|
||||
*/
|
||||
if (ptr) RNA_id_pointer_create((ID *)ob, ptr);
|
||||
return &ob->gpd;
|
||||
}
|
||||
else {
|
||||
/* Scene Case (default):
|
||||
* This is the new (as of 2014-Oct-13, for 2.73) default setting
|
||||
* which should work better for most users.
|
||||
*/
|
||||
if (ptr) RNA_id_pointer_create((ID *)scene, ptr);
|
||||
return &scene->gpd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPACE_NODE: /* Nodes Editor */
|
||||
{
|
||||
SpaceNode *snode = (SpaceNode *)CTX_wm_space_data(C);
|
||||
SpaceNode *snode = (SpaceNode *)sl;
|
||||
|
||||
/* return the GP data for the active node block/node */
|
||||
if (snode && snode->nodetree) {
|
||||
@@ -128,7 +144,7 @@ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
|
||||
}
|
||||
case SPACE_SEQ: /* Sequencer */
|
||||
{
|
||||
SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C);
|
||||
SpaceSeq *sseq = (SpaceSeq *)sl;
|
||||
|
||||
/* for now, Grease Pencil data is associated with the space (actually preview region only) */
|
||||
/* XXX our convention for everything else is to link to data though... */
|
||||
@@ -137,7 +153,7 @@ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
|
||||
}
|
||||
case SPACE_IMAGE: /* Image/UV Editor */
|
||||
{
|
||||
SpaceImage *sima = (SpaceImage *)CTX_wm_space_data(C);
|
||||
SpaceImage *sima = (SpaceImage *)sl;
|
||||
|
||||
/* for now, Grease Pencil data is associated with the space... */
|
||||
/* XXX our convention for everything else is to link to data though... */
|
||||
@@ -146,7 +162,7 @@ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
|
||||
}
|
||||
case SPACE_CLIP: /* Nodes Editor */
|
||||
{
|
||||
SpaceClip *sc = (SpaceClip *)CTX_wm_space_data(C);
|
||||
SpaceClip *sc = (SpaceClip *)sl;
|
||||
MovieClip *clip = ED_space_clip_get_clip(sc);
|
||||
|
||||
if (clip) {
|
||||
@@ -180,6 +196,26 @@ bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
|
||||
return (scene) ? &scene->gpd : NULL;
|
||||
}
|
||||
|
||||
/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
|
||||
bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
ID *screen_id = (ID *)CTX_wm_screen(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
||||
return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
|
||||
/* Get the active Grease Pencil datablock, when context is not available */
|
||||
bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob)
|
||||
{
|
||||
bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, NULL);
|
||||
return (gpd_ptr) ? *(gpd_ptr) : NULL;
|
||||
}
|
||||
|
||||
/* Get the active Grease Pencil datablock */
|
||||
bGPdata *ED_gpencil_data_get_active(const bContext *C)
|
||||
{
|
||||
@@ -187,6 +223,8 @@ bGPdata *ED_gpencil_data_get_active(const bContext *C)
|
||||
return (gpd_ptr) ? *(gpd_ptr) : NULL;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
|
||||
bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene, View3D *v3d)
|
||||
{
|
||||
Base *base = scene->basact;
|
||||
@@ -211,6 +249,15 @@ static int gp_add_poll(bContext *C)
|
||||
return ED_gpencil_data_get_pointers(C, NULL) != NULL;
|
||||
}
|
||||
|
||||
/* poll callback for checking if there is an active layer */
|
||||
static int gp_active_layer_poll(bContext *C)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
|
||||
|
||||
return (gpl != NULL);
|
||||
}
|
||||
|
||||
/* ******************* Add New Data ************************ */
|
||||
|
||||
/* add new datablock - wrapper around API */
|
||||
@@ -327,13 +374,268 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot)
|
||||
ot->name = "Add New Layer";
|
||||
ot->idname = "GPENCIL_OT_layer_add";
|
||||
ot->description = "Add new Grease Pencil layer for the active Grease Pencil datablock";
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_layer_add_exec;
|
||||
ot->poll = gp_add_poll;
|
||||
}
|
||||
|
||||
/* ******************* Remove Active Layer ************************* */
|
||||
|
||||
static int gp_layer_remove_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
|
||||
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, gpd, gpl))
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
if (gpl->flag & GP_LAYER_LOCKED) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* make the layer before this the new active layer
|
||||
* - use the one after if this is the first
|
||||
* - if this is the only layer, this naturally becomes NULL
|
||||
*/
|
||||
if (gpl->prev)
|
||||
gpencil_layer_setactive(gpd, gpl->prev);
|
||||
else
|
||||
gpencil_layer_setactive(gpd, gpl->next);
|
||||
|
||||
/* delete the layer now... */
|
||||
gpencil_layer_delete(gpd, gpl);
|
||||
|
||||
/* notifiers */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_layer_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Layer";
|
||||
ot->idname = "GPENCIL_OT_layer_remove";
|
||||
ot->description = "Remove active Grease Pencil layer";
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_layer_remove_exec;
|
||||
ot->poll = gp_active_layer_poll;
|
||||
}
|
||||
|
||||
/* ******************* Move Layer Up/Down ************************** */
|
||||
|
||||
enum {
|
||||
GP_LAYER_MOVE_UP = -1,
|
||||
GP_LAYER_MOVE_DOWN = 1
|
||||
};
|
||||
|
||||
static int gp_layer_move_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
|
||||
|
||||
int direction = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, gpd, gpl))
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* up or down? */
|
||||
if (direction == GP_LAYER_MOVE_UP) {
|
||||
/* up */
|
||||
BLI_remlink(&gpd->layers, gpl);
|
||||
BLI_insertlinkbefore(&gpd->layers, gpl->prev, gpl);
|
||||
}
|
||||
else {
|
||||
/* down */
|
||||
BLI_remlink(&gpd->layers, gpl);
|
||||
BLI_insertlinkafter(&gpd->layers, gpl->next, gpl);
|
||||
}
|
||||
|
||||
/* notifiers */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_layer_move(wmOperatorType *ot)
|
||||
{
|
||||
static EnumPropertyItem slot_move[] = {
|
||||
{GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
|
||||
{GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Move Grease Pencil Layer";
|
||||
ot->idname = "GPENCIL_OT_layer_move";
|
||||
ot->description = "Move the active Grease Pencil layer up/down in the list";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = gp_layer_move_exec;
|
||||
ot->poll = gp_active_layer_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
|
||||
}
|
||||
|
||||
/* ************************************************ */
|
||||
/* Stroke Editing Operators */
|
||||
|
||||
/* poll callback for all stroke editing operators */
|
||||
static int gp_stroke_edit_poll(bContext *C)
|
||||
{
|
||||
/* NOTE: this is a bit slower, but is the most accurate... */
|
||||
return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
|
||||
}
|
||||
|
||||
/* ************** Duplicate Selected Strokes **************** */
|
||||
|
||||
/* Make copies of selected point segments in a selected stroke */
|
||||
static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
int start_idx = -1;
|
||||
|
||||
|
||||
/* Step through the original stroke's points:
|
||||
* - We accumulate selected points (from start_idx to current index)
|
||||
* and then convert that to a new stroke
|
||||
*/
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
/* searching for start, are waiting for end? */
|
||||
if (start_idx == -1) {
|
||||
/* is this the first selected point for a new island? */
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
start_idx = i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t len = 0;
|
||||
|
||||
/* is this the end of current island yet?
|
||||
* 1) Point i-1 was the last one that was selected
|
||||
* 2) Point i is the last in the array
|
||||
*/
|
||||
if ((pt->flag & GP_SPOINT_SELECT) == 0) {
|
||||
len = i - start_idx;
|
||||
}
|
||||
else if (i == gps->totpoints - 1) {
|
||||
len = i - start_idx + 1;
|
||||
}
|
||||
//printf("copying from %d to %d = %d\n", start_idx, i, len);
|
||||
|
||||
/* make copies of the relevant data */
|
||||
if (len) {
|
||||
bGPDstroke *gpsd;
|
||||
|
||||
/* make a stupid copy first of the entire stroke (to get the flags too) */
|
||||
gpsd = MEM_dupallocN(gps);
|
||||
|
||||
/* now, make a new points array, and copy of the relevant parts */
|
||||
gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
|
||||
memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
|
||||
gpsd->totpoints = len;
|
||||
|
||||
/* add to temp buffer */
|
||||
gpsd->next = gpsd->prev = NULL;
|
||||
BLI_addtail(new_strokes, gpsd);
|
||||
|
||||
/* cleanup + reset for next */
|
||||
start_idx = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gp_duplicate_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
|
||||
if (gpd == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* for each visible (and editable) layer's selected strokes,
|
||||
* copy the strokes into a temporary buffer, then append
|
||||
* once all done
|
||||
*/
|
||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||
{
|
||||
ListBase new_strokes = {NULL, NULL};
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps;
|
||||
|
||||
if (gpf == NULL)
|
||||
continue;
|
||||
|
||||
/* make copies of selected strokes, and deselect these once we're done */
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
if (gps->totpoints == 1) {
|
||||
/* Special Case: If there's just a single point in this stroke... */
|
||||
bGPDstroke *gpsd;
|
||||
|
||||
/* make direct copies of the stroke and its points */
|
||||
gpsd = MEM_dupallocN(gps);
|
||||
gpsd->points = MEM_dupallocN(gps->points);
|
||||
|
||||
/* add to temp buffer */
|
||||
gpsd->next = gpsd->prev = NULL;
|
||||
BLI_addtail(&new_strokes, gpsd);
|
||||
}
|
||||
else {
|
||||
/* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
|
||||
gp_duplicate_points(gps, &new_strokes);
|
||||
}
|
||||
|
||||
/* deselect original stroke, or else the originals get moved too
|
||||
* (when using the copy + move macro)
|
||||
*/
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
/* add all new strokes in temp buffer to the frame (preventing double-copies) */
|
||||
BLI_movelisttolist(&gpf->strokes, &new_strokes);
|
||||
BLI_assert(new_strokes.first == NULL);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* updates */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_duplicate(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Duplicate Strokes";
|
||||
ot->idname = "GPENCIL_OT_duplicate";
|
||||
ot->description = "Duplicate the selected Grease Pencil strokes";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gp_duplicate_exec;
|
||||
ot->poll = gp_stroke_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ******************* Delete Active Frame ************************ */
|
||||
|
||||
static int gp_actframe_delete_poll(bContext *C)
|
||||
@@ -378,6 +680,7 @@ void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
|
||||
ot->name = "Delete Active Frame";
|
||||
ot->idname = "GPENCIL_OT_active_frame_delete";
|
||||
ot->description = "Delete the active frame for the active Grease Pencil datablock";
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* callbacks */
|
||||
@@ -385,6 +688,306 @@ void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
|
||||
ot->poll = gp_actframe_delete_poll;
|
||||
}
|
||||
|
||||
/* ******************* Delete Operator ************************ */
|
||||
|
||||
typedef enum eGP_DeleteMode {
|
||||
/* delete selected stroke points */
|
||||
GP_DELETEOP_POINTS = 0,
|
||||
/* delete selected strokes */
|
||||
GP_DELETEOP_STROKES = 1,
|
||||
/* delete active frame */
|
||||
GP_DELETEOP_FRAME = 2,
|
||||
/* delete selected stroke points (without splitting stroke) */
|
||||
GP_DELETEOP_POINTS_DISSOLVE = 3,
|
||||
} eGP_DeleteMode;
|
||||
|
||||
|
||||
/* Delete selected strokes */
|
||||
static int gp_delete_selected_strokes(bContext *C)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||
{
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps, *gpsn;
|
||||
|
||||
if (gpf == NULL)
|
||||
continue;
|
||||
|
||||
/* simply delete strokes which are selected */
|
||||
for (gps = gpf->strokes.first; gps; gps = gpsn) {
|
||||
gpsn = gps->next;
|
||||
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
/* free stroke memory arrays, then stroke itself */
|
||||
if (gps->points) MEM_freeN(gps->points);
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
if (changed) {
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
else {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete selected points but keep the stroke */
|
||||
static int gp_dissolve_selected_points(bContext *C)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||
{
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps, *gpsn;
|
||||
|
||||
if (gpf == NULL)
|
||||
continue;
|
||||
|
||||
/* simply delete points from selected strokes
|
||||
* NOTE: we may still have to remove the stroke if it ends up having no points!
|
||||
*/
|
||||
for (gps = gpf->strokes.first; gps; gps = gpsn) {
|
||||
gpsn = gps->next;
|
||||
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
int tot = gps->totpoints; /* number of points in new buffer */
|
||||
|
||||
/* First Pass: Count how many points are selected (i.e. how many to remove) */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
/* selected point - one of the points to remove */
|
||||
tot--;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no points are left, we simply delete the entire stroke */
|
||||
if (tot <= 0) {
|
||||
/* remove the entire stroke */
|
||||
MEM_freeN(gps->points);
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
}
|
||||
else {
|
||||
/* just copy all unselected into a smaller buffer */
|
||||
bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
|
||||
bGPDspoint *npt = new_points;
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if ((pt->flag & GP_SPOINT_SELECT) == 0) {
|
||||
*npt = *pt;
|
||||
npt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* free the old buffer */
|
||||
MEM_freeN(gps->points);
|
||||
|
||||
/* save the new buffer */
|
||||
gps->points = new_points;
|
||||
gps->totpoints = tot;
|
||||
|
||||
/* deselect the stroke, since none of its selected points will still be selected */
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
if (changed) {
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
else {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Split selected strokes into segments, splitting on selected points */
|
||||
static int gp_delete_selected_points(bContext *C)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||
{
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps, *gpsn;
|
||||
|
||||
if (gpf == NULL)
|
||||
continue;
|
||||
|
||||
/* simply delete strokes which are selected */
|
||||
for (gps = gpf->strokes.first; gps; gps = gpsn) {
|
||||
gpsn = gps->next;
|
||||
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* 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);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
if (changed) {
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
else {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int gp_delete_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
|
||||
int result = OPERATOR_CANCELLED;
|
||||
|
||||
switch (mode) {
|
||||
case GP_DELETEOP_STROKES: /* selected strokes */
|
||||
result = gp_delete_selected_strokes(C);
|
||||
break;
|
||||
|
||||
case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */
|
||||
result = gp_delete_selected_points(C);
|
||||
break;
|
||||
|
||||
case GP_DELETEOP_POINTS_DISSOLVE: /* selected points (without splitting the stroke) */
|
||||
result = gp_dissolve_selected_points(C);
|
||||
break;
|
||||
|
||||
case GP_DELETEOP_FRAME: /* active frame */
|
||||
result = gp_actframe_delete_exec(C, op);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_delete(wmOperatorType *ot)
|
||||
{
|
||||
static EnumPropertyItem prop_gpencil_delete_types[] = {
|
||||
{GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
|
||||
{GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
|
||||
{GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
|
||||
{0, "", 0, NULL, NULL},
|
||||
{GP_DELETEOP_POINTS_DISSOLVE, "DISSOLVE_POINTS", 0, "Dissolve Points", "Delete selected points without splitting strokesp"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Delete...";
|
||||
ot->idname = "GPENCIL_OT_delete";
|
||||
ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->exec = gp_delete_exec;
|
||||
ot->poll = gp_stroke_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
|
||||
/* props */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
|
||||
}
|
||||
|
||||
/* ************************************************ */
|
||||
/* Grease Pencil to Data Operator */
|
||||
|
||||
|
@@ -31,14 +31,73 @@
|
||||
#ifndef __GPENCIL_INTERN_H__
|
||||
#define __GPENCIL_INTERN_H__
|
||||
|
||||
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
|
||||
/* internal exports only */
|
||||
struct bGPdata;
|
||||
struct bGPDstroke;
|
||||
struct bGPDspoint;
|
||||
|
||||
struct ARegion;
|
||||
struct View2D;
|
||||
struct wmOperatorType;
|
||||
|
||||
|
||||
/* ***************************************************** */
|
||||
/* Operator Defines */
|
||||
/* Internal API */
|
||||
|
||||
struct bGPdata;
|
||||
struct wmOperatorType;
|
||||
/* Stroke Coordinates API ------------------------------ */
|
||||
/* gpencil_utils.c */
|
||||
|
||||
typedef struct GP_SpaceConversion {
|
||||
struct bGPdata *gpd;
|
||||
struct bGPDlayer *gpl;
|
||||
|
||||
struct ScrArea *sa;
|
||||
struct ARegion *ar;
|
||||
struct View2D *v2d;
|
||||
|
||||
rctf *subrect; /* for using the camera rect within the 3d view */
|
||||
rctf subrect_data;
|
||||
|
||||
float mat[4][4]; /* transform matrix on the strokes (introduced in [b770964]) */
|
||||
} GP_SpaceConversion;
|
||||
|
||||
|
||||
/**
|
||||
* Check whether a given stroke segment is inside a circular brush
|
||||
*
|
||||
* \param mval The current screen-space coordinates (midpoint) of the brush
|
||||
* \param mvalo The previous screen-space coordinates (midpoint) of the brush (NOT CURRENTLY USED)
|
||||
* \param rad The radius of the brush
|
||||
*
|
||||
* \param x0, y0 The screen-space x and y coordinates of the start of the stroke segment
|
||||
* \param x1, y1 The screen-space x and y coordinates of the end of the stroke segment
|
||||
*/
|
||||
bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]),
|
||||
int rad, int x0, int y0, int x1, int y1);
|
||||
|
||||
|
||||
/**
|
||||
* Init settings for stroke point space conversions
|
||||
*
|
||||
* \param[out] r_gsc The space conversion settings struct, populated with necessary params
|
||||
*/
|
||||
void gp_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc);
|
||||
|
||||
/**
|
||||
* Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D)
|
||||
*
|
||||
* \param[out] r_x The screen-space x-coordinate of the point
|
||||
* \param[out] r_y The screen-space y-coordinate of the point
|
||||
*/
|
||||
void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct bGPDspoint *pt,
|
||||
int *r_x, int *r_y);
|
||||
|
||||
/* ***************************************************** */
|
||||
/* Operator Defines */
|
||||
|
||||
/* drawing ---------- */
|
||||
|
||||
@@ -52,12 +111,28 @@ typedef enum eGPencil_PaintModes {
|
||||
GP_PAINTMODE_DRAW_POLY
|
||||
} eGPencil_PaintModes;
|
||||
|
||||
/* stroke editing ----- */
|
||||
|
||||
void GPENCIL_OT_select(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_select_all(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_select_circle(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_select_border(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_select_linked(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_select_more(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_select_less(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_duplicate(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_delete(struct wmOperatorType *ot);
|
||||
|
||||
/* buttons editing --- */
|
||||
|
||||
void GPENCIL_OT_data_add(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_data_unlink(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_layer_add(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_layer_remove(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_layer_move(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot);
|
||||
|
||||
|
@@ -34,40 +34,159 @@
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "DNA_gpencil_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_transform.h"
|
||||
|
||||
#include "gpencil_intern.h"
|
||||
|
||||
/* ****************************************** */
|
||||
/* Generic Editing Keymap */
|
||||
/* Grease Pencil Keymaps */
|
||||
|
||||
void ED_keymap_gpencil(wmKeyConfig *keyconf)
|
||||
/* Generic Drawing Keymap */
|
||||
static void ed_keymap_gpencil_general(wmKeyConfig *keyconf)
|
||||
{
|
||||
wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil", 0, 0);
|
||||
wmKeyMapItem *kmi;
|
||||
|
||||
/* Draw */
|
||||
|
||||
/* Draw --------------------------------------- */
|
||||
/* draw */
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, DKEY);
|
||||
RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW);
|
||||
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
|
||||
#if 0 // XXX: disabled, since they won't be of any use anymore
|
||||
/* draw - straight lines */
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, DKEY);
|
||||
RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_STRAIGHT);
|
||||
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
|
||||
/* draw - poly lines */
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", RIGHTMOUSE, KM_PRESS, KM_CTRL, DKEY);
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_ALT, DKEY);
|
||||
RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_POLY);
|
||||
|
||||
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
|
||||
#endif
|
||||
|
||||
/* erase */
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", RIGHTMOUSE, KM_PRESS, 0, DKEY);
|
||||
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");
|
||||
|
||||
/* Pie Menu - For standard tools */
|
||||
WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_tool_palette", DKEY, KM_PRESS, KM_CTRL, 0);
|
||||
WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_settings_palette", QKEY, KM_PRESS, 0, DKEY);
|
||||
}
|
||||
|
||||
/* ==================== */
|
||||
|
||||
/* Poll callback for stroke editing mode */
|
||||
static int gp_stroke_editmode_poll(bContext *C)
|
||||
{
|
||||
bGPdata *gpd = CTX_data_gpencil_data(C);
|
||||
return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE));
|
||||
}
|
||||
|
||||
/* Stroke Editing Keymap - Only when editmode is enabled */
|
||||
static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
|
||||
{
|
||||
wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0);
|
||||
wmKeyMapItem *kmi;
|
||||
|
||||
/* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */
|
||||
keymap->poll = gp_stroke_editmode_poll;
|
||||
|
||||
/* Selection ------------------------------------- */
|
||||
/* select all */
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_all", AKEY, KM_PRESS, 0, 0);
|
||||
RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
|
||||
RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
|
||||
|
||||
/* circle select */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_select_circle", CKEY, KM_PRESS, 0, 0);
|
||||
|
||||
/* border select */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_select_border", BKEY, KM_PRESS, 0, 0);
|
||||
|
||||
/* normal select */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
|
||||
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "extend", true);
|
||||
RNA_boolean_set(kmi->ptr, "toggle", true);
|
||||
|
||||
/* whole stroke select */
|
||||
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_ALT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "entire_strokes", true);
|
||||
|
||||
/* select linked */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_select_linked", LKEY, KM_PRESS, KM_CTRL, 0);
|
||||
|
||||
/* select more/less */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0);
|
||||
|
||||
|
||||
/* Editing ----------------------------------------- */
|
||||
|
||||
/* duplicate and move selected points */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
|
||||
/* delete */
|
||||
WM_keymap_add_item(keymap, "GPENCIL_OT_delete", XKEY, KM_PRESS, 0, 0);
|
||||
|
||||
|
||||
/* 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_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);
|
||||
|
||||
/* Proportional Editing */
|
||||
ED_keymap_proportional_cycle(keyconf, keymap);
|
||||
ED_keymap_proportional_editmode(keyconf, keymap, true);
|
||||
}
|
||||
|
||||
/* ==================== */
|
||||
|
||||
void ED_keymap_gpencil(wmKeyConfig *keyconf)
|
||||
{
|
||||
ed_keymap_gpencil_general(keyconf);
|
||||
ed_keymap_gpencil_editing(keyconf);
|
||||
}
|
||||
|
||||
/* ****************************************** */
|
||||
@@ -78,12 +197,28 @@ void ED_operatortypes_gpencil(void)
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_draw);
|
||||
|
||||
/* Editing (Strokes) ------------ */
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_select);
|
||||
WM_operatortype_append(GPENCIL_OT_select_all);
|
||||
WM_operatortype_append(GPENCIL_OT_select_circle);
|
||||
WM_operatortype_append(GPENCIL_OT_select_border);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_select_linked);
|
||||
WM_operatortype_append(GPENCIL_OT_select_more);
|
||||
WM_operatortype_append(GPENCIL_OT_select_less);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_duplicate);
|
||||
WM_operatortype_append(GPENCIL_OT_delete);
|
||||
|
||||
/* Editing (Buttons) ------------ */
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_data_add);
|
||||
WM_operatortype_append(GPENCIL_OT_data_unlink);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_layer_add);
|
||||
WM_operatortype_append(GPENCIL_OT_layer_remove);
|
||||
WM_operatortype_append(GPENCIL_OT_layer_move);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_active_frame_delete);
|
||||
|
||||
@@ -92,4 +227,17 @@ void ED_operatortypes_gpencil(void)
|
||||
/* Editing (Time) --------------- */
|
||||
}
|
||||
|
||||
void ED_operatormacros_gpencil(void)
|
||||
{
|
||||
wmOperatorType *ot;
|
||||
wmOperatorTypeMacro *otmacro;
|
||||
|
||||
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_enum_set(otmacro->ptr, "gpencil_strokes", true);
|
||||
}
|
||||
|
||||
/* ****************************************** */
|
||||
|
@@ -86,6 +86,8 @@ typedef struct tGPsdata {
|
||||
rctf *subrect; /* for using the camera rect within the 3d view */
|
||||
rctf subrect_data;
|
||||
|
||||
GP_SpaceConversion gsc; /* settings to pass to gp_points_to_xy() */
|
||||
|
||||
PointerRNA ownerPtr; /* pointer to owner of gp-datablock */
|
||||
bGPdata *gpd; /* gp-datablock layer comes from */
|
||||
bGPDlayer *gpl; /* layer we're working on */
|
||||
@@ -875,58 +877,6 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* eraser tool - check if part of stroke occurs within last segment drawn by eraser */
|
||||
static short gp_stroke_eraser_strokeinside(const int mval[2], const int UNUSED(mvalo[2]),
|
||||
int rad, int x0, int y0, int x1, int y1)
|
||||
{
|
||||
/* simple within-radius check for now */
|
||||
const float mval_fl[2] = {mval[0], mval[1]};
|
||||
const float screen_co_a[2] = {x0, y0};
|
||||
const float screen_co_b[2] = {x1, y1};
|
||||
|
||||
if (edge_inside_circle(mval_fl, rad, screen_co_a, screen_co_b)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* not inside */
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gp_point_to_xy(tGPsdata *p, bGPDstroke *gps, bGPDspoint *pt,
|
||||
int *r_x, int *r_y)
|
||||
{
|
||||
ARegion *ar = p->ar;
|
||||
View2D *v2d = p->v2d;
|
||||
rctf *subrect = p->subrect;
|
||||
int xyval[2];
|
||||
|
||||
if (gps->flag & GP_STROKE_3DSPACE) {
|
||||
if (ED_view3d_project_int_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
|
||||
*r_x = xyval[0];
|
||||
*r_y = xyval[1];
|
||||
}
|
||||
else {
|
||||
*r_x = V2D_IS_CLIPPED;
|
||||
*r_y = V2D_IS_CLIPPED;
|
||||
}
|
||||
}
|
||||
else if (gps->flag & GP_STROKE_2DSPACE) {
|
||||
float vec[3] = {pt->x, pt->y, 0.0f};
|
||||
mul_m4_v3(p->mat, vec);
|
||||
UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y);
|
||||
}
|
||||
else {
|
||||
if (subrect == NULL) { /* normal 3D view */
|
||||
*r_x = (int)(pt->x / 100 * ar->winx);
|
||||
*r_y = (int)(pt->y / 100 * ar->winy);
|
||||
}
|
||||
else { /* camera view, use subrect */
|
||||
*r_x = (int)((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin;
|
||||
*r_y = (int)((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* eraser tool - evaluation per stroke */
|
||||
/* TODO: this could really do with some optimization (KD-Tree/BVH?) */
|
||||
@@ -945,7 +895,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
|
||||
BLI_freelinkN(&gpf->strokes, gps);
|
||||
}
|
||||
else if (gps->totpoints == 1) {
|
||||
gp_point_to_xy(p, gps, gps->points, &x0, &y0);
|
||||
gp_point_to_xy(&p->gsc, gps, gps->points, &x0, &y0);
|
||||
|
||||
/* do boundbox check first */
|
||||
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
|
||||
@@ -966,8 +916,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
|
||||
pt1 = gps->points + i;
|
||||
pt2 = gps->points + i + 1;
|
||||
|
||||
gp_point_to_xy(p, gps, pt1, &x0, &y0);
|
||||
gp_point_to_xy(p, gps, pt2, &x1, &y1);
|
||||
gp_point_to_xy(&p->gsc, gps, pt1, &x0, &y0);
|
||||
gp_point_to_xy(&p->gsc, gps, pt2, &x1, &y1);
|
||||
|
||||
/* 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)) ||
|
||||
@@ -977,7 +927,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
|
||||
* eraser region (either within stroke painted, or on its lines)
|
||||
* - this assumes that linewidth is irrelevant
|
||||
*/
|
||||
if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) {
|
||||
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))
|
||||
{
|
||||
@@ -1068,6 +1018,7 @@ static int gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
p->win = CTX_wm_window(C);
|
||||
|
||||
unit_m4(p->imat);
|
||||
unit_m4(p->mat);
|
||||
|
||||
switch (curarea->spacetype) {
|
||||
/* supported views first */
|
||||
@@ -1154,7 +1105,9 @@ static int gp_session_initdata(bContext *C, tGPsdata *p)
|
||||
p->imat[3][0] -= marker->pos[0];
|
||||
p->imat[3][1] -= marker->pos[1];
|
||||
}
|
||||
|
||||
invert_m4_m4(p->mat, p->imat);
|
||||
copy_m4_m4(p->gsc.mat, p->mat);
|
||||
break;
|
||||
}
|
||||
/* unsupported views */
|
||||
@@ -1290,7 +1243,21 @@ static void gp_paint_initstroke(tGPsdata *p, short paintmode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* init stroke point space-conversion settings... */
|
||||
p->gsc.gpd = p->gpd;
|
||||
p->gsc.gpl = p->gpl;
|
||||
|
||||
p->gsc.sa = p->sa;
|
||||
p->gsc.ar = p->ar;
|
||||
p->gsc.v2d = p->v2d;
|
||||
|
||||
p->gsc.subrect_data = p->subrect_data;
|
||||
p->gsc.subrect = p->subrect;
|
||||
|
||||
copy_m4_m4(p->gsc.mat, p->mat);
|
||||
|
||||
|
||||
/* check if points will need to be made in view-aligned space */
|
||||
if (p->gpd->flag & GP_DATA_VIEWALIGN) {
|
||||
switch (p->sa->spacetype) {
|
||||
@@ -1772,11 +1739,8 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
|
||||
else
|
||||
WM_cursor_modal_set(win, BC_PAINTBRUSHCURSOR);
|
||||
|
||||
/* special hack: if there was an initial event, then we were invoked via a hotkey, and
|
||||
* painting should start immediately. Otherwise, this was called from a toolbar, in which
|
||||
* case we should wait for the mouse to be clicked.
|
||||
*/
|
||||
if (event->val == KM_PRESS) {
|
||||
/* only start drawing immediately if we're allowed to do so... */
|
||||
if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
|
||||
/* hotkey invoked - start drawing */
|
||||
/* printf("\tGP - set first spot\n"); */
|
||||
p->status = GP_STATUS_PAINTING;
|
||||
@@ -1868,8 +1832,11 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
|
||||
/* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */
|
||||
if (ISKEYBOARD(event->type)) {
|
||||
if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY)) {
|
||||
/* allow some keys - for frame changing: [#33412] */
|
||||
if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) {
|
||||
/* allow some keys:
|
||||
* - for frame changing [#33412]
|
||||
* - for undo (during sketching sessions)
|
||||
*/
|
||||
}
|
||||
else {
|
||||
estate = OPERATOR_RUNNING_MODAL;
|
||||
@@ -2053,6 +2020,8 @@ void GPENCIL_OT_draw(wmOperatorType *ot)
|
||||
|
||||
/* settings for drawing */
|
||||
ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements");
|
||||
|
||||
RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
|
||||
|
||||
/* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */
|
||||
RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately");
|
||||
}
|
||||
|
839
source/blender/editors/gpencil/gpencil_select.c
Normal file
839
source/blender/editors/gpencil/gpencil_select.c
Normal file
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2014, Blender Foundation
|
||||
* This is a new part of Blender
|
||||
*
|
||||
* Contributor(s): Joshua Leung
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/gpencil/gpencil_select.c
|
||||
* \ingroup edgpencil
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_view3d.h"
|
||||
#include "ED_keyframing.h"
|
||||
|
||||
#include "gpencil_intern.h"
|
||||
|
||||
/* ********************************************** */
|
||||
/* Polling callbacks */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Select All Operator */
|
||||
|
||||
static int gpencil_select_all_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
int action = RNA_enum_get(op->ptr, "action");
|
||||
|
||||
if (gpd == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* for "toggle", test for existing selected strokes */
|
||||
if (action == SEL_TOGGLE) {
|
||||
action = SEL_SELECT;
|
||||
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
action = SEL_DESELECT;
|
||||
break; // XXX: this only gets out of the inner loop...
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
|
||||
/* if deselecting, we need to deselect strokes across all frames
|
||||
* - Currently, an exception is only given for deselection
|
||||
* Selecting and toggling should only affect what's visible,
|
||||
* while deselecting helps clean up unintended/forgotten
|
||||
* stuff on other frames
|
||||
*/
|
||||
if (action == SEL_DESELECT) {
|
||||
/* deselect strokes across editable layers
|
||||
* NOTE: we limit ourselves to editable layers, since once a layer is "locked/hidden
|
||||
* nothing should be able to touch it
|
||||
*/
|
||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||
{
|
||||
bGPDframe *gpf;
|
||||
|
||||
/* deselect all strokes on all frames */
|
||||
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||
bGPDstroke *gps;
|
||||
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
else {
|
||||
/* select or deselect all strokes */
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
bool selected = false;
|
||||
|
||||
/* Change selection status of all points, then make the stroke match */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
switch (action) {
|
||||
case SEL_SELECT:
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
break;
|
||||
//case SEL_DESELECT:
|
||||
// pt->flag &= ~GP_SPOINT_SELECT;
|
||||
// break;
|
||||
case SEL_INVERT:
|
||||
pt->flag ^= GP_SPOINT_SELECT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pt->flag & GP_SPOINT_SELECT)
|
||||
selected = true;
|
||||
}
|
||||
|
||||
/* Change status of stroke */
|
||||
if (selected)
|
||||
gps->flag |= GP_STROKE_SELECT;
|
||||
else
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
|
||||
/* updates */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "(De)select All Strokes";
|
||||
ot->idname = "GPENCIL_OT_select_all";
|
||||
ot->description = "Change selection of all Grease Pencil strokes currently visible";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gpencil_select_all_exec;
|
||||
ot->poll = gpencil_select_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
WM_operator_properties_select_all(ot);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Select Linked */
|
||||
|
||||
static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
|
||||
if (gpd == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* select all points in selected strokes */
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* updates */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_linked(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select Linked";
|
||||
ot->idname = "GPENCIL_OT_select_linked";
|
||||
ot->description = "Select all points in same strokes as already selected points";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gpencil_select_linked_exec;
|
||||
ot->poll = gpencil_select_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Select More */
|
||||
|
||||
static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
bool prev_sel;
|
||||
|
||||
/* First Pass: Go in forward order, expanding selection if previous was selected (pre changes)...
|
||||
* - This pass covers the "after" edges of selection islands
|
||||
*/
|
||||
prev_sel = false;
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
/* selected point - just set flag for next point */
|
||||
prev_sel = true;
|
||||
}
|
||||
else {
|
||||
/* unselected point - expand selection if previous was selected... */
|
||||
if (prev_sel) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
prev_sel = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
|
||||
* - This pass covers the "before" edges of selection islands
|
||||
*/
|
||||
prev_sel = false;
|
||||
for (pt -= 1; i > 0; i--, pt--) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
prev_sel = true;
|
||||
}
|
||||
else {
|
||||
/* unselected point - expand selection if previous was selected... */
|
||||
if (prev_sel) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
prev_sel = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* updates */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_more(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select More";
|
||||
ot->idname = "GPENCIL_OT_select_more";
|
||||
ot->description = "Grow sets of selected Grease Pencil points";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gpencil_select_more_exec;
|
||||
ot->poll = gpencil_select_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Select Less */
|
||||
|
||||
static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
bool prev_sel;
|
||||
|
||||
/* First Pass: Go in forward order, shrinking selection if previous was not selected (pre changes)...
|
||||
* - This pass covers the "after" edges of selection islands
|
||||
*/
|
||||
prev_sel = false;
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
/* shrink if previous wasn't selected */
|
||||
if (prev_sel == false) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
prev_sel = true;
|
||||
}
|
||||
else {
|
||||
/* mark previous as being unselected - and hence, is trigger for shrinking */
|
||||
prev_sel = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
|
||||
* - This pass covers the "before" edges of selection islands
|
||||
*/
|
||||
prev_sel = false;
|
||||
for (pt -= 1; i > 0; i--, pt--) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
/* shrink if previous wasn't selected */
|
||||
if (prev_sel == false) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
prev_sel = true;
|
||||
}
|
||||
else {
|
||||
/* mark previous as being unselected - and hence, is trigger for shrinking */
|
||||
prev_sel = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* updates */
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_less(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select Less";
|
||||
ot->idname = "GPENCIL_OT_select_less";
|
||||
ot->description = "Shrink sets of selected Grease Pencil points";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = gpencil_select_less_exec;
|
||||
ot->poll = gpencil_select_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Circle Select Operator */
|
||||
|
||||
/* Helper to check if a given stroke is within the area */
|
||||
/* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke()
|
||||
* It would be great to de-duplicate the logic here sometime, but that can wait...
|
||||
*/
|
||||
static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
|
||||
const int mx, const int my, const int radius,
|
||||
const bool select, rcti *rect)
|
||||
{
|
||||
bGPDspoint *pt1, *pt2;
|
||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
int i;
|
||||
bool changed = false;
|
||||
|
||||
if (gps->totpoints == 1) {
|
||||
gp_point_to_xy(gsc, gps, gps->points, &x0, &y0);
|
||||
|
||||
/* do boundbox check first */
|
||||
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
|
||||
/* only check if point is inside */
|
||||
if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) {
|
||||
/* change selection */
|
||||
if (select) {
|
||||
gps->points->flag |= GP_SPOINT_SELECT;
|
||||
gps->flag |= GP_STROKE_SELECT;
|
||||
}
|
||||
else {
|
||||
gps->points->flag &= ~GP_SPOINT_SELECT;
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Loop over the points in the stroke, checking for intersections
|
||||
* - an intersection means that we touched the stroke
|
||||
*/
|
||||
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(gsc, gps, pt1, &x0, &y0);
|
||||
gp_point_to_xy(gsc, gps, pt2, &x1, &y1);
|
||||
|
||||
/* check that point segment of the boundbox of the selection 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)))
|
||||
{
|
||||
int mval[2] = {mx, my};
|
||||
int mvalo[2] = {mx, my}; /* dummy - this isn't used... */
|
||||
|
||||
/* 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, radius, x0, y0, x1, y1)) {
|
||||
/* change selection of stroke, and then of both points
|
||||
* (as the last point otherwise wouldn't get selected
|
||||
* as we only do n-1 loops through)
|
||||
*/
|
||||
if (select) {
|
||||
pt1->flag |= GP_SPOINT_SELECT;
|
||||
pt2->flag |= GP_SPOINT_SELECT;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
else {
|
||||
pt1->flag &= ~GP_SPOINT_SELECT;
|
||||
pt2->flag &= ~GP_SPOINT_SELECT;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure that stroke selection is in sync with its points */
|
||||
gpencil_stroke_sync_selection(gps);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
|
||||
const int mx = RNA_int_get(op->ptr, "x");
|
||||
const int my = RNA_int_get(op->ptr, "y");
|
||||
const int radius = RNA_int_get(op->ptr, "radius");
|
||||
|
||||
const int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
|
||||
const bool select = (gesture_mode == GESTURE_MODAL_SELECT);
|
||||
|
||||
GP_SpaceConversion gsc = {0};
|
||||
rcti rect = {0}; /* for bounding rect around circle (for quicky intersection testing) */
|
||||
|
||||
bool changed = false;
|
||||
|
||||
|
||||
/* sanity checks */
|
||||
if (sa == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active area");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* init space conversion stuff */
|
||||
gp_point_conversion_init(C, &gsc);
|
||||
|
||||
|
||||
/* rect is rectangle of selection circle */
|
||||
rect.xmin = mx - radius;
|
||||
rect.ymin = my - radius;
|
||||
rect.xmax = mx + radius;
|
||||
rect.ymax = my + radius;
|
||||
|
||||
|
||||
/* find visible strokes, and select if hit */
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
changed |= gp_stroke_do_circle_sel(gps, &gsc, mx, my, radius, select, &rect);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* updates */
|
||||
if (changed) {
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_circle(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Circle Select";
|
||||
ot->description = "Select Grease Pencil strokes using brush selection";
|
||||
ot->idname = "GPENCIL_OT_select_circle";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = WM_gesture_circle_invoke;
|
||||
ot->modal = WM_gesture_circle_modal;
|
||||
ot->exec = gpencil_circle_select_exec;
|
||||
ot->poll = gpencil_select_poll;
|
||||
ot->cancel = WM_gesture_circle_cancel;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
|
||||
RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
|
||||
RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX);
|
||||
RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Box Selection */
|
||||
|
||||
static int gpencil_border_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
|
||||
const int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
|
||||
const bool select = (gesture_mode == GESTURE_MODAL_SELECT);
|
||||
const bool extend = RNA_boolean_get(op->ptr, "extend");
|
||||
|
||||
GP_SpaceConversion gsc = {0};
|
||||
rcti rect = {0};
|
||||
|
||||
bool changed = false;
|
||||
|
||||
|
||||
/* sanity checks */
|
||||
if (sa == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active area");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* init space conversion stuff */
|
||||
gp_point_conversion_init(C, &gsc);
|
||||
|
||||
|
||||
/* deselect all strokes first? */
|
||||
if (select && !extend) {
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
|
||||
/* get settings from operator */
|
||||
WM_operator_properties_border_to_rcti(op, &rect);
|
||||
|
||||
/* select/deselect points */
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
int x0, y0;
|
||||
|
||||
/* convert point coords to screenspace */
|
||||
gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
|
||||
|
||||
/* test if in selection rect */
|
||||
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) {
|
||||
if (select) {
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
else {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure that stroke selection is in sync with its points */
|
||||
gpencil_stroke_sync_selection(gps);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* updates */
|
||||
if (changed) {
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select_border(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Border Select";
|
||||
ot->description = "Select Grease Pencil strokes within a rectangular region";
|
||||
ot->idname = "GPENCIL_OT_select_border";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = WM_border_select_invoke;
|
||||
ot->exec = gpencil_border_select_exec;
|
||||
ot->modal = WM_border_select_modal;
|
||||
ot->cancel = WM_border_select_cancel;
|
||||
|
||||
ot->poll = gpencil_select_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* rna */
|
||||
WM_operator_properties_gesture_border(ot, true);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Mouse Click to Select */
|
||||
|
||||
static int gpencil_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
|
||||
/* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */
|
||||
const float radius = 0.75f * U.widget_unit;
|
||||
const int radius_squared = (int)(radius * radius);
|
||||
|
||||
bool extend = RNA_boolean_get(op->ptr, "extend");
|
||||
bool deselect = RNA_boolean_get(op->ptr, "deselect");
|
||||
bool toggle = RNA_boolean_get(op->ptr, "toggle");
|
||||
bool whole = RNA_boolean_get(op->ptr, "entire_strokes");
|
||||
|
||||
int location[2] = {0};
|
||||
int mx, my;
|
||||
|
||||
GP_SpaceConversion gsc = {0};
|
||||
|
||||
bGPDstroke *hit_stroke = NULL;
|
||||
bGPDspoint *hit_point = NULL;
|
||||
|
||||
/* sanity checks */
|
||||
if (sa == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active area");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* init space conversion stuff */
|
||||
gp_point_conversion_init(C, &gsc);
|
||||
|
||||
/* get mouse location */
|
||||
RNA_int_get_array(op->ptr, "location", location);
|
||||
|
||||
mx = location[0];
|
||||
my = location[1];
|
||||
|
||||
/* First Pass: Find stroke point which gets hit */
|
||||
/* XXX: maybe we should go from the top of the stack down instead... */
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
int hit_index = -1;
|
||||
|
||||
/* firstly, check for hit-point */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
int x0, y0;
|
||||
|
||||
gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
|
||||
|
||||
/* do boundbox check first */
|
||||
if (!ELEM(V2D_IS_CLIPPED, x0, x0)) {
|
||||
/* only check if point is inside */
|
||||
if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius_squared) {
|
||||
hit_stroke = gps;
|
||||
hit_point = pt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* skip to next stroke if nothing found */
|
||||
if (hit_index == -1)
|
||||
continue;
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* Abort if nothing hit... */
|
||||
if (ELEM(NULL, hit_stroke, hit_point)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* adjust selection behaviour - for toggle option */
|
||||
if (toggle) {
|
||||
deselect = (hit_point->flag & GP_SPOINT_SELECT) != 0;
|
||||
}
|
||||
|
||||
/* If not extending selection, deselect everything else */
|
||||
if (extend == false) {
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
/* deselect stroke and its points if selected */
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* deselect points */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
/* deselect stroke itself too */
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
|
||||
/* Perform selection operations... */
|
||||
if (whole) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* entire stroke's points */
|
||||
for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
|
||||
if (deselect == false)
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
else
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
/* stroke too... */
|
||||
if (deselect == false)
|
||||
hit_stroke->flag |= GP_STROKE_SELECT;
|
||||
else
|
||||
hit_stroke->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
else {
|
||||
/* just the point (and the stroke) */
|
||||
if (deselect == false) {
|
||||
/* we're adding selection, so selection must be true */
|
||||
hit_point->flag |= GP_SPOINT_SELECT;
|
||||
hit_stroke->flag |= GP_STROKE_SELECT;
|
||||
}
|
||||
else {
|
||||
/* deselect point */
|
||||
hit_point->flag &= ~GP_SPOINT_SELECT;
|
||||
|
||||
/* ensure that stroke is selected correctly */
|
||||
gpencil_stroke_sync_selection(hit_stroke);
|
||||
}
|
||||
}
|
||||
|
||||
/* updates */
|
||||
if (hit_point != NULL) {
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
RNA_int_set_array(op->ptr, "location", event->mval);
|
||||
return gpencil_select_exec(C, op);
|
||||
}
|
||||
|
||||
void GPENCIL_OT_select(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Select";
|
||||
ot->description = "Select Grease Pencil strokes and/or stroke points";
|
||||
ot->idname = "GPENCIL_OT_select";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = gpencil_select_invoke;
|
||||
ot->exec = gpencil_select_exec;
|
||||
ot->poll = gpencil_select_poll;
|
||||
|
||||
/* flag */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
WM_operator_properties_mouse_select(ot);
|
||||
|
||||
prop = RNA_def_boolean(ot->srna, "entire_strokes", false, "Entire Strokes", "Select entire strokes instead of just the nearest stroke vertex");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, INT_MIN, INT_MAX, "Location", "Mouse location", INT_MIN, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
|
||||
|
@@ -133,7 +133,12 @@ void gpencil_undo_push(bGPdata *gpd)
|
||||
|
||||
while (undo_node) {
|
||||
bGPundonode *next_node = undo_node->next;
|
||||
|
||||
|
||||
/* HACK: animdata wasn't duplicated, so it shouldn't be freed here,
|
||||
* or else the real copy will segfault when accessed
|
||||
*/
|
||||
undo_node->gpd->adt = NULL;
|
||||
|
||||
BKE_gpencil_free(undo_node->gpd);
|
||||
MEM_freeN(undo_node->gpd);
|
||||
|
||||
@@ -157,6 +162,11 @@ void gpencil_undo_finish(void)
|
||||
bGPundonode *undo_node = undo_nodes.first;
|
||||
|
||||
while (undo_node) {
|
||||
/* HACK: animdata wasn't duplicated, so it shouldn't be freed here,
|
||||
* or else the real copy will segfault when accessed
|
||||
*/
|
||||
undo_node->gpd->adt = NULL;
|
||||
|
||||
BKE_gpencil_free(undo_node->gpd);
|
||||
MEM_freeN(undo_node->gpd);
|
||||
|
||||
|
166
source/blender/editors/gpencil/gpencil_utils.c
Normal file
166
source/blender/editors/gpencil/gpencil_utils.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2014, Blender Foundation
|
||||
*
|
||||
* Contributor(s): Joshua Leung
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/gpencil/gpencil_utils.c
|
||||
* \ingroup edgpencil
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
#include "gpencil_intern.h"
|
||||
|
||||
/* ******************************************************** */
|
||||
|
||||
/* Check if part of stroke occurs within last segment drawn by eraser */
|
||||
bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]),
|
||||
int rad, int x0, int y0, int x1, int y1)
|
||||
{
|
||||
/* simple within-radius check for now */
|
||||
const float mval_fl[2] = {mval[0], mval[1]};
|
||||
const float screen_co_a[2] = {x0, y0};
|
||||
const float screen_co_b[2] = {x1, y1};
|
||||
|
||||
if (edge_inside_circle(mval_fl, rad, screen_co_a, screen_co_b)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* not inside */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ******************************************************** */
|
||||
|
||||
/* Init handling for space-conversion function (from passed-in parameters) */
|
||||
void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
|
||||
{
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
|
||||
/* zero out the storage (just in case) */
|
||||
memset(r_gsc, 0, sizeof(GP_SpaceConversion));
|
||||
unit_m4(r_gsc->mat);
|
||||
|
||||
/* store settings */
|
||||
r_gsc->sa = sa;
|
||||
r_gsc->ar = ar;
|
||||
r_gsc->v2d = &ar->v2d;
|
||||
|
||||
/* init region-specific stuff */
|
||||
if (sa->spacetype == SPACE_VIEW3D) {
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
View3D *v3d = (View3D *)CTX_wm_space_data(C);
|
||||
RegionView3D *rv3d = ar->regiondata;
|
||||
|
||||
/* init 3d depth buffers */
|
||||
view3d_operator_needs_opengl(C);
|
||||
|
||||
view3d_region_operator_needs_opengl(win, ar);
|
||||
ED_view3d_autodist_init(scene, ar, v3d, 0);
|
||||
|
||||
/* for camera view set the subrect */
|
||||
if (rv3d->persp == RV3D_CAMOB) {
|
||||
ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &r_gsc->subrect_data, true); /* no shift */
|
||||
r_gsc->subrect = &r_gsc->subrect_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Convert Grease Pencil points to screen-space values */
|
||||
void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt,
|
||||
int *r_x, int *r_y)
|
||||
{
|
||||
ARegion *ar = gsc->ar;
|
||||
View2D *v2d = gsc->v2d;
|
||||
rctf *subrect = gsc->subrect;
|
||||
int xyval[2];
|
||||
|
||||
if (gps->flag & GP_STROKE_3DSPACE) {
|
||||
if (ED_view3d_project_int_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
|
||||
*r_x = xyval[0];
|
||||
*r_y = xyval[1];
|
||||
}
|
||||
else {
|
||||
*r_x = V2D_IS_CLIPPED;
|
||||
*r_y = V2D_IS_CLIPPED;
|
||||
}
|
||||
}
|
||||
else if (gps->flag & GP_STROKE_2DSPACE) {
|
||||
float vec[3] = {pt->x, pt->y, 0.0f};
|
||||
mul_m4_v3(gsc->mat, vec);
|
||||
UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y);
|
||||
}
|
||||
else {
|
||||
if (subrect == NULL) { /* normal 3D view */
|
||||
*r_x = (int)(pt->x / 100 * ar->winx);
|
||||
*r_y = (int)(pt->y / 100 * ar->winy);
|
||||
}
|
||||
else { /* camera view, use subrect */
|
||||
*r_x = (int)((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin;
|
||||
*r_y = (int)((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************** */
|
@@ -159,6 +159,7 @@ typedef enum eAnim_ChannelType {
|
||||
ANIMTYPE_DSLAT,
|
||||
ANIMTYPE_DSLINESTYLE,
|
||||
ANIMTYPE_DSSPK,
|
||||
ANIMTYPE_DSGPENCIL,
|
||||
|
||||
ANIMTYPE_SHAPEKEY,
|
||||
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#ifndef __ED_GPENCIL_H__
|
||||
#define __ED_GPENCIL_H__
|
||||
|
||||
struct ID;
|
||||
struct ListBase;
|
||||
struct bContext;
|
||||
struct bScreen;
|
||||
@@ -38,6 +39,7 @@ struct ARegion;
|
||||
struct View3D;
|
||||
struct SpaceNode;
|
||||
struct SpaceSeq;
|
||||
struct Object;
|
||||
struct bGPdata;
|
||||
struct bGPDlayer;
|
||||
struct bGPDframe;
|
||||
@@ -65,14 +67,26 @@ typedef struct tGPspoint {
|
||||
|
||||
/* ----------- Grease Pencil Tools/Context ------------- */
|
||||
|
||||
/* Context-dependent */
|
||||
struct bGPdata **ED_gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *ptr);
|
||||
struct bGPdata *ED_gpencil_data_get_active(const struct bContext *C);
|
||||
|
||||
/* Context independent (i.e. each required part is passed in instead) */
|
||||
struct bGPdata **ED_gpencil_data_get_pointers_direct(struct ID *screen_id, struct Scene *scene,
|
||||
struct ScrArea *sa, struct Object *ob,
|
||||
struct PointerRNA *ptr);
|
||||
struct bGPdata *ED_gpencil_data_get_active_direct(struct ID *screen_id, struct Scene *scene,
|
||||
struct ScrArea *sa, struct Object *ob);
|
||||
|
||||
/* 3D View */
|
||||
struct bGPdata *ED_gpencil_data_get_active_v3d(struct Scene *scene, struct View3D *v3d);
|
||||
|
||||
/* ----------- Grease Pencil Operators ----------------- */
|
||||
|
||||
void ED_keymap_gpencil(struct wmKeyConfig *keyconf);
|
||||
|
||||
void ED_operatortypes_gpencil(void);
|
||||
void ED_operatormacros_gpencil(void);
|
||||
|
||||
/* ------------ Grease-Pencil Drawing API ------------------ */
|
||||
/* drawgpencil.c */
|
||||
@@ -99,6 +113,8 @@ void ED_gpencil_select_frame(struct bGPDlayer *gpl, int selx, short select_mode
|
||||
bool ED_gplayer_frames_delete(struct bGPDlayer *gpl);
|
||||
void ED_gplayer_frames_duplicate(struct bGPDlayer *gpl);
|
||||
|
||||
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
|
||||
|
@@ -120,8 +120,9 @@ void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Obje
|
||||
void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos);
|
||||
/* DopeSheet Summary */
|
||||
void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos);
|
||||
/* Grease Pencil Layer */
|
||||
// XXX not restored
|
||||
/* Grease Pencil datablock summary */
|
||||
void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPdata *gpd, float ypos);
|
||||
/* Grease Pencil Layer */
|
||||
void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos);
|
||||
/* Mask Layer */
|
||||
void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos);
|
||||
@@ -139,11 +140,11 @@ void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree
|
||||
void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
|
||||
/* DopeSheet Summary */
|
||||
void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
|
||||
/* Grease Pencil datablock summary */
|
||||
void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, struct DLRBT_Tree *keys);
|
||||
/* Grease Pencil Layer */
|
||||
// XXX not restored
|
||||
void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys);
|
||||
/* Mask */
|
||||
// XXX not restored
|
||||
void mask_to_keylist(struct bDopeSheet *UNUSED(ads), struct MaskLayer *masklay, struct DLRBT_Tree *keys);
|
||||
|
||||
/* ActKeyColumn API ---------------- */
|
||||
|
@@ -99,6 +99,7 @@ enum TfmMode {
|
||||
#define CTX_MOVIECLIP (1 << 6)
|
||||
#define CTX_MASK (1 << 7)
|
||||
#define CTX_PAINT_CURVE (1 << 8)
|
||||
#define CTX_GPENCIL_STROKES (1 << 9)
|
||||
|
||||
/* Standalone call to get the transformation center corresponding to the current situation
|
||||
* returns 1 if successful, 0 otherwise (usually means there's no selection)
|
||||
@@ -146,6 +147,7 @@ int BIF_countTransformOrientation(const struct bContext *C);
|
||||
#define P_CORRECT_UV (1 << 8)
|
||||
#define P_NO_DEFAULTS (1 << 10)
|
||||
#define P_NO_TEXSPACE (1 << 11)
|
||||
#define P_GPENCIL_EDIT (1 << 12)
|
||||
|
||||
void Transform_Properties(struct wmOperatorType *ot, int flags);
|
||||
|
||||
|
@@ -186,7 +186,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
|
||||
wmOrtho2(0, sizex, 0, sizey);
|
||||
glTranslatef(sizex / 2, sizey / 2, 0.0f);
|
||||
|
||||
G.f |= G_RENDER_OGL;
|
||||
ED_gpencil_draw_ex(gpd, sizex, sizey, scene->r.cfra);
|
||||
G.f &= ~G_RENDER_OGL;
|
||||
|
||||
gp_rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect");
|
||||
GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect);
|
||||
|
@@ -1351,8 +1351,18 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand
|
||||
}
|
||||
if (flag & ED_KEYMAP_GPENCIL) {
|
||||
/* grease pencil */
|
||||
wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Grease Pencil", 0, 0);
|
||||
WM_event_add_keymap_handler(handlers, keymap);
|
||||
/* NOTE: This is now 2 keymaps - One for basic functionality,
|
||||
* and one that only applies when "Edit Mode" is enabled
|
||||
* for strokes.
|
||||
*
|
||||
* For now, it's easier to just include both,
|
||||
* since you hardly want one without the other.
|
||||
*/
|
||||
wmKeyMap *keymap_general = WM_keymap_find(wm->defaultconf, "Grease Pencil", 0, 0);
|
||||
wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
|
||||
|
||||
WM_event_add_keymap_handler(handlers, keymap_general);
|
||||
WM_event_add_keymap_handler(handlers, keymap_edit);
|
||||
}
|
||||
if (flag & ED_KEYMAP_HEADER) {
|
||||
/* standard keymap for headers regions */
|
||||
|
@@ -26,12 +26,13 @@
|
||||
* \ingroup edscr
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
@@ -45,11 +46,13 @@
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_sequencer.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "ED_armature.h"
|
||||
#include "ED_gpencil.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "UI_interface.h"
|
||||
@@ -66,12 +69,16 @@ const char *screen_context_dir[] = {
|
||||
"sculpt_object", "vertex_paint_object", "weight_paint_object",
|
||||
"image_paint_object", "particle_edit_object",
|
||||
"sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */
|
||||
"gpencil_data", "gpencil_data_owner", /* grease pencil data */
|
||||
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
|
||||
"active_gpencil_layer", "active_gpencil_frame",
|
||||
"active_operator",
|
||||
NULL};
|
||||
|
||||
int ed_screen_context(const bContext *C, const char *member, bContextDataResult *result)
|
||||
{
|
||||
bScreen *sc = CTX_wm_screen(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
Scene *scene = sc->scene;
|
||||
Base *base;
|
||||
unsigned int lay = scene->lay;
|
||||
@@ -392,6 +399,112 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "gpencil_data")) {
|
||||
/* FIXME: for some reason, CTX_data_active_object(C) returns NULL when called from these situations
|
||||
* (as outlined above - see Campbell's #ifdefs). That causes the get active function to fail when
|
||||
* called from context. For that reason, we end up using an alternative where we pass everything in!
|
||||
*/
|
||||
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
|
||||
|
||||
if (gpd) {
|
||||
CTX_data_id_pointer_set(result, &gpd->id);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "gpencil_data_owner")) {
|
||||
/* pointer to which data/datablock owns the reference to the Grease Pencil data being used (as gpencil_data)
|
||||
* XXX: see comment for gpencil_data case...
|
||||
*/
|
||||
bGPdata **gpd_ptr = NULL;
|
||||
PointerRNA ptr;
|
||||
|
||||
/* get pointer to Grease Pencil Data */
|
||||
gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, scene, sa, obact, &ptr);
|
||||
|
||||
if (gpd_ptr) {
|
||||
CTX_data_pointer_set(result, ptr.id.data, ptr.type, ptr.data);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "active_gpencil_layer")) {
|
||||
/* XXX: see comment for gpencil_data case... */
|
||||
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
|
||||
|
||||
if (gpd) {
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
|
||||
|
||||
if (gpl) {
|
||||
CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "active_gpencil_frame")) {
|
||||
/* XXX: see comment for gpencil_data case... */
|
||||
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
|
||||
|
||||
if (gpd) {
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
|
||||
|
||||
if (gpl) {
|
||||
CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl->actframe);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "visible_gpencil_layers")) {
|
||||
/* XXX: see comment for gpencil_data case... */
|
||||
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
|
||||
|
||||
if (gpd) {
|
||||
bGPDlayer *gpl;
|
||||
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
if ((gpl->flag & GP_LAYER_HIDE) == 0) {
|
||||
CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl);
|
||||
}
|
||||
}
|
||||
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "editable_gpencil_layers")) {
|
||||
/* XXX: see comment for gpencil_data case... */
|
||||
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
|
||||
|
||||
if (gpd) {
|
||||
bGPDlayer *gpl;
|
||||
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0) {
|
||||
CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl);
|
||||
}
|
||||
}
|
||||
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "editable_gpencil_strokes")) {
|
||||
/* XXX: see comment for gpencil_data case... */
|
||||
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
|
||||
|
||||
if (gpd) {
|
||||
bGPDlayer *gpl;
|
||||
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0 && (gpl->actframe)) {
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps;
|
||||
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps);
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (CTX_data_equals(member, "active_operator")) {
|
||||
wmOperator *op = NULL;
|
||||
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_mask_types.h"
|
||||
@@ -2148,6 +2149,7 @@ 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;
|
||||
@@ -2175,7 +2177,9 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
|
||||
|
||||
if (ob)
|
||||
ob_to_keylist(&ads, ob, &keys, NULL);
|
||||
|
||||
|
||||
gpencil_to_keylist(&ads, gpd, &keys);
|
||||
|
||||
{
|
||||
Mask *mask = CTX_data_edit_mask(C);
|
||||
if (mask) {
|
||||
|
@@ -965,8 +965,11 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op)
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
|
||||
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Not implemented");
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* get cleaning threshold */
|
||||
thresh = RNA_float_get(op->ptr, "threshold");
|
||||
@@ -1025,15 +1028,18 @@ static void sample_action_keys(bAnimContext *ac)
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
static int actkeys_sample_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
static int actkeys_sample_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bAnimContext ac;
|
||||
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
|
||||
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Not implemented");
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* sample keyframes */
|
||||
sample_action_keys(&ac);
|
||||
@@ -1138,8 +1144,11 @@ static int actkeys_expo_exec(bContext *C, wmOperator *op)
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
|
||||
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Not implemented");
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* get handle setting mode */
|
||||
mode = RNA_enum_get(op->ptr, "type");
|
||||
@@ -1209,8 +1218,11 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op)
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
|
||||
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Not implemented");
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* get handle setting mode */
|
||||
mode = RNA_enum_get(op->ptr, "type");
|
||||
@@ -1288,8 +1300,11 @@ static int actkeys_handletype_exec(bContext *C, wmOperator *op)
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
|
||||
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Not implemented");
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* get handle setting mode */
|
||||
mode = RNA_enum_get(op->ptr, "type");
|
||||
@@ -1324,7 +1339,7 @@ void ACTION_OT_handle_type(wmOperatorType *ot)
|
||||
|
||||
/* ******************** Set Keyframe-Type Operator *********************** */
|
||||
|
||||
/* this function is responsible for setting interpolation mode for keyframes */
|
||||
/* this function is responsible for setting keyframe type for keyframes */
|
||||
static void setkeytype_action_keys(bAnimContext *ac, short mode)
|
||||
{
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
@@ -1349,6 +1364,29 @@ static void setkeytype_action_keys(bAnimContext *ac, short mode)
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
/* this function is responsible for setting the keyframe type for Grease Pencil frames */
|
||||
static void setkeytype_gpencil_keys(bAnimContext *ac, short mode)
|
||||
{
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
bAnimListElem *ale;
|
||||
int filter;
|
||||
|
||||
/* filter data */
|
||||
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
|
||||
|
||||
/* loop through each layer */
|
||||
for (ale = anim_data.first; ale; ale = ale->next) {
|
||||
if (ale->type == ANIMTYPE_GPLAYER) {
|
||||
ED_gplayer_frames_keytype_set(ale->data, mode);
|
||||
ale->update |= ANIM_UPDATE_DEPS;
|
||||
}
|
||||
}
|
||||
|
||||
ANIM_animdata_update(ac, &anim_data);
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
static int actkeys_keytype_exec(bContext *C, wmOperator *op)
|
||||
@@ -1359,14 +1397,22 @@ static int actkeys_keytype_exec(bContext *C, wmOperator *op)
|
||||
/* get editor data */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0)
|
||||
return OPERATOR_CANCELLED;
|
||||
if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
|
||||
|
||||
if (ac.datatype == ANIMCONT_MASK) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Not implemented for Masks");
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* get handle setting mode */
|
||||
mode = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
/* set handle type */
|
||||
setkeytype_action_keys(&ac, mode);
|
||||
if (ac.datatype == ANIMCONT_GPENCIL) {
|
||||
setkeytype_gpencil_keys(&ac, mode);
|
||||
}
|
||||
else {
|
||||
setkeytype_action_keys(&ac, mode);
|
||||
}
|
||||
|
||||
/* set notifier that keyframe properties have changed */
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
|
||||
|
@@ -153,6 +153,7 @@ void ED_spacemacros_init(void)
|
||||
ED_operatormacros_mask();
|
||||
ED_operatormacros_sequencer();
|
||||
ED_operatormacros_paint();
|
||||
ED_operatormacros_gpencil();
|
||||
|
||||
/* register dropboxes (can use macros) */
|
||||
spacetypes = BKE_spacetypes_list();
|
||||
|
@@ -69,15 +69,18 @@
|
||||
|
||||
/* Panels */
|
||||
|
||||
#if 0
|
||||
static int clip_grease_pencil_panel_poll(const bContext *C, PanelType *UNUSED(pt))
|
||||
{
|
||||
SpaceClip *sc = CTX_wm_space_clip(C);
|
||||
|
||||
return sc->view == SC_VIEW_CLIP;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ED_clip_buttons_register(ARegionType *art)
|
||||
{
|
||||
#if 0
|
||||
PanelType *pt;
|
||||
|
||||
pt = MEM_callocN(sizeof(PanelType), "spacetype clip panel gpencil");
|
||||
@@ -89,6 +92,7 @@ void ED_clip_buttons_register(ARegionType *art)
|
||||
pt->flag |= PNL_DEFAULT_CLOSED;
|
||||
pt->poll = clip_grease_pencil_panel_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
#endif
|
||||
}
|
||||
|
||||
/********************* MovieClip Template ************************/
|
||||
|
@@ -420,6 +420,9 @@ static void clip_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
|
||||
clip_scopes_check_gpencil_change(sa);
|
||||
ED_area_tag_redraw(sa);
|
||||
}
|
||||
else if (wmn->data & ND_GPENCIL_EDITMODE) {
|
||||
ED_area_tag_redraw(sa);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1245,6 +1248,8 @@ static void clip_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), AR
|
||||
case NC_GPENCIL:
|
||||
if (wmn->action == NA_EDITED)
|
||||
ED_region_tag_redraw(ar);
|
||||
else if (wmn->data & ND_GPENCIL_EDITMODE)
|
||||
ED_region_tag_redraw(ar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1495,7 +1500,7 @@ static void clip_properties_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(s
|
||||
/* context changes */
|
||||
switch (wmn->category) {
|
||||
case NC_GPENCIL:
|
||||
if (wmn->data == ND_DATA)
|
||||
if (ELEM(wmn->data, ND_DATA, ND_GPENCIL_EDITMODE))
|
||||
ED_region_tag_redraw(ar);
|
||||
break;
|
||||
case NC_BRUSH:
|
||||
|
@@ -979,6 +979,7 @@ void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser
|
||||
|
||||
void image_buttons_register(ARegionType *art)
|
||||
{
|
||||
#if 0
|
||||
PanelType *pt;
|
||||
const char *category = "Grease Pencil";
|
||||
|
||||
@@ -990,6 +991,7 @@ void image_buttons_register(ARegionType *art)
|
||||
pt->draw = ED_gpencil_panel_standard;
|
||||
BLI_strncpy(pt->category, category, BLI_strlen_utf8(category));
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int image_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
|
@@ -143,6 +143,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p
|
||||
case ANIMTYPE_DSLAT:
|
||||
case ANIMTYPE_DSLINESTYLE:
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
/* for these channels, we only do AnimData */
|
||||
if (ale->adt && adt_ptr) {
|
||||
|
@@ -182,6 +182,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe
|
||||
case ANIMTYPE_DSLAT:
|
||||
case ANIMTYPE_DSLINESTYLE:
|
||||
case ANIMTYPE_DSSPK:
|
||||
case ANIMTYPE_DSGPENCIL:
|
||||
{
|
||||
/* sanity checking... */
|
||||
if (ale->adt) {
|
||||
|
@@ -57,6 +57,7 @@
|
||||
|
||||
/* ******************* node space & buttons ************** */
|
||||
|
||||
#if 0
|
||||
/* poll for active nodetree */
|
||||
static int active_nodetree_poll(const bContext *C, PanelType *UNUSED(pt))
|
||||
{
|
||||
@@ -64,6 +65,7 @@ static int active_nodetree_poll(const bContext *C, PanelType *UNUSED(pt))
|
||||
|
||||
return (snode && snode->nodetree);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int node_sockets_poll(const bContext *C, PanelType *UNUSED(pt))
|
||||
{
|
||||
@@ -198,6 +200,7 @@ void node_buttons_register(ARegionType *art)
|
||||
pt->poll = node_tree_interface_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
|
||||
#if 0
|
||||
pt = MEM_callocN(sizeof(PanelType), "spacetype node panel gpencil");
|
||||
strcpy(pt->idname, "NODE_PT_gpencil");
|
||||
strcpy(pt->label, N_("Grease Pencil"));
|
||||
@@ -206,6 +209,7 @@ void node_buttons_register(ARegionType *art)
|
||||
pt->draw = ED_gpencil_panel_standard;
|
||||
pt->poll = active_nodetree_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int node_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
|
@@ -513,6 +513,11 @@ static void node_area_listener(bScreen *sc, ScrArea *sa, wmNotifier *wmn)
|
||||
ED_area_tag_refresh(sa);
|
||||
}
|
||||
break;
|
||||
case NC_GPENCIL:
|
||||
if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
|
||||
ED_area_tag_redraw(sa);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,6 +771,8 @@ static void node_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegi
|
||||
case NC_GPENCIL:
|
||||
if (wmn->action == NA_EDITED)
|
||||
ED_region_tag_redraw(ar);
|
||||
else if (wmn->data & ND_GPENCIL_EDITMODE)
|
||||
ED_region_tag_redraw(ar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -51,6 +51,7 @@
|
||||
|
||||
/* **************************** buttons ********************************* */
|
||||
|
||||
#if 0
|
||||
static int sequencer_grease_pencil_panel_poll(const bContext *C, PanelType *UNUSED(pt))
|
||||
{
|
||||
SpaceSeq *sseq = CTX_wm_space_seq(C);
|
||||
@@ -58,9 +59,11 @@ static int sequencer_grease_pencil_panel_poll(const bContext *C, PanelType *UNUS
|
||||
/* don't show the gpencil if we are not showing the image */
|
||||
return ED_space_sequencer_check_show_imbuf(sseq);
|
||||
}
|
||||
#endif
|
||||
|
||||
void sequencer_buttons_register(ARegionType *art)
|
||||
{
|
||||
#if 0
|
||||
PanelType *pt;
|
||||
|
||||
pt = MEM_callocN(sizeof(PanelType), "spacetype sequencer panel gpencil");
|
||||
@@ -71,6 +74,7 @@ void sequencer_buttons_register(ARegionType *art)
|
||||
pt->draw = ED_gpencil_panel_standard;
|
||||
pt->poll = sequencer_grease_pencil_panel_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* **************** operator to open/close properties view ************* */
|
||||
|
@@ -352,6 +352,10 @@ static void sequencer_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn
|
||||
if (wmn->data == ND_SPACE_SEQUENCER)
|
||||
sequencer_scopes_tag_refresh(sa);
|
||||
break;
|
||||
case NC_GPENCIL:
|
||||
if (wmn->data & ND_GPENCIL_EDITMODE)
|
||||
ED_area_tag_redraw(sa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,7 +589,7 @@ static void sequencer_preview_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED
|
||||
/* context changes */
|
||||
switch (wmn->category) {
|
||||
case NC_GPENCIL:
|
||||
if (wmn->action == NA_EDITED) {
|
||||
if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
|
||||
ED_region_tag_redraw(ar);
|
||||
}
|
||||
break;
|
||||
@@ -641,7 +645,7 @@ static void sequencer_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED
|
||||
/* context changes */
|
||||
switch (wmn->category) {
|
||||
case NC_GPENCIL:
|
||||
if (wmn->data == ND_DATA) {
|
||||
if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
|
||||
ED_region_tag_redraw(ar);
|
||||
}
|
||||
break;
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
@@ -310,6 +311,9 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel)
|
||||
case ID_OB:
|
||||
ob_to_keylist(&ads, (Object *)id, &keys, NULL);
|
||||
break;
|
||||
case ID_GD:
|
||||
gpencil_to_keylist(&ads, (bGPdata *)id, &keys);
|
||||
break;
|
||||
}
|
||||
|
||||
/* build linked-list for searching */
|
||||
@@ -339,9 +343,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) {
|
||||
glColor3ub(0xB5, 0xE6, 0x1D);
|
||||
time_draw_idblock_keyframes(v2d, (ID *)gpd, onlysel);
|
||||
}
|
||||
|
||||
/* draw scene keyframes first
|
||||
* - don't try to do this when only drawing active/selected data keyframes,
|
||||
* since this can become quite slow
|
||||
|
@@ -950,8 +950,9 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN
|
||||
|
||||
break;
|
||||
case NC_GPENCIL:
|
||||
if (wmn->action == NA_EDITED)
|
||||
if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
|
||||
ED_region_tag_redraw(ar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1007,6 +1008,10 @@ static void view3d_header_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa)
|
||||
if (wmn->data == ND_SPACE_VIEW3D)
|
||||
ED_region_tag_redraw(ar);
|
||||
break;
|
||||
case NC_GPENCIL:
|
||||
if (wmn->data & ND_GPENCIL_EDITMODE)
|
||||
ED_region_tag_redraw(ar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,7 +1109,7 @@ static void view3d_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa
|
||||
ED_region_tag_redraw(ar);
|
||||
break;
|
||||
case NC_GPENCIL:
|
||||
if (wmn->data == ND_DATA || wmn->action == NA_EDITED)
|
||||
if ((wmn->data & (ND_DATA | ND_GPENCIL_EDITMODE)) || (wmn->action == NA_EDITED))
|
||||
ED_region_tag_redraw(ar);
|
||||
break;
|
||||
case NC_IMAGE:
|
||||
|
@@ -1185,6 +1185,7 @@ void view3d_buttons_register(ARegionType *art)
|
||||
pt->poll = view3d_panel_transform_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
|
||||
#if 0
|
||||
pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel gpencil");
|
||||
strcpy(pt->idname, "VIEW3D_PT_gpencil");
|
||||
strcpy(pt->label, N_("Grease Pencil")); /* XXX C panels are not available through RNA (bpy.types)! */
|
||||
@@ -1192,6 +1193,7 @@ void view3d_buttons_register(ARegionType *art)
|
||||
pt->draw_header = ED_gpencil_panel_standard_header;
|
||||
pt->draw = ED_gpencil_panel_standard;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
#endif
|
||||
|
||||
pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel vgroup");
|
||||
strcpy(pt->idname, "VIEW3D_PT_vgroup");
|
||||
|
@@ -2013,6 +2013,12 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
|
||||
options |= CTX_TEXTURE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) && RNA_property_is_set(op->ptr, prop)) {
|
||||
if (RNA_property_boolean_get(op->ptr, prop)) {
|
||||
options |= CTX_GPENCIL_STROKES;
|
||||
}
|
||||
}
|
||||
|
||||
t->options = options;
|
||||
|
||||
|
@@ -105,6 +105,7 @@
|
||||
#include "ED_uvedit.h"
|
||||
#include "ED_clip.h"
|
||||
#include "ED_mask.h"
|
||||
#include "ED_gpencil.h"
|
||||
|
||||
#include "WM_api.h" /* for WM_event_add_notifier to deal with stabilization nodes */
|
||||
#include "WM_types.h"
|
||||
@@ -5613,7 +5614,10 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
|
||||
}
|
||||
|
||||
|
||||
if (t->spacetype == SPACE_SEQ) {
|
||||
if (t->options & CTX_GPENCIL_STROKES) {
|
||||
/* pass */
|
||||
}
|
||||
else if (t->spacetype == SPACE_SEQ) {
|
||||
/* freeSeqData in transform_conversions.c does this
|
||||
* keep here so the else at the end wont run... */
|
||||
|
||||
@@ -7280,6 +7284,230 @@ void flushTransPaintCurve(TransInfo *t)
|
||||
}
|
||||
|
||||
|
||||
static void createTransGPencil(bContext *C, TransInfo *t)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
bGPDlayer *gpl;
|
||||
TransData *td = NULL;
|
||||
float mtx[3][3], smtx[3][3];
|
||||
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
const int cfra = CFRA;
|
||||
|
||||
const int propedit = (t->flag & T_PROP_EDIT);
|
||||
const int propedit_connected = (t->flag & T_PROP_CONNECTED);
|
||||
|
||||
|
||||
/* == Grease Pencil Strokes to Transform Data ==
|
||||
* Grease Pencil stroke points can be a mixture of 2D (screen-space),
|
||||
* or 3D coordinates. However, they're always saved as 3D points.
|
||||
* For now, we just do these without creating TransData2D for the 2D
|
||||
* strokes. This may cause issues in future though.
|
||||
*/
|
||||
t->total = 0;
|
||||
|
||||
if (gpd == NULL)
|
||||
return;
|
||||
|
||||
/* First Pass: Count the number of datapoints required for the strokes,
|
||||
* (and additional info about the configuration - e.g. 2D/3D?)
|
||||
*/
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
/* only editable and visible layers are considered */
|
||||
if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0 &&
|
||||
(gpl->actframe != NULL))
|
||||
{
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps;
|
||||
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
if (propedit) {
|
||||
/* Proportional Editing... */
|
||||
if (propedit_connected) {
|
||||
/* connected only - so only if selected */
|
||||
if (gps->flag & GP_STROKE_SELECT)
|
||||
t->total += gps->totpoints;
|
||||
}
|
||||
else {
|
||||
/* everything goes - connection status doesn't matter */
|
||||
t->total += gps->totpoints;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* only selected stroke points are considered */
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
// TODO: 2D vs 3D?
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT)
|
||||
t->total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop trying if nothing selected */
|
||||
if (t->total == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate memory for data */
|
||||
t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(GPencil)");
|
||||
td = t->data;
|
||||
|
||||
unit_m3(smtx);
|
||||
unit_m3(mtx);
|
||||
|
||||
/* Second Pass: Build transdata array */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
/* only editable and visible layers are considered */
|
||||
if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0 &&
|
||||
(gpl->actframe != NULL))
|
||||
{
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
bGPDstroke *gps;
|
||||
|
||||
/* Make a new frame to work on if the layer's frame and the current scene frame don't match up
|
||||
* - This is useful when animating as it saves that "uh-oh" moment when you realise you've
|
||||
* spent too much time editing the wrong frame...
|
||||
*/
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop over strokes, adding TransData for points as needed... */
|
||||
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
TransData *head = td;
|
||||
TransData *tail = td;
|
||||
bool stroke_ok;
|
||||
|
||||
/* What we need to include depends on proportional editing settings... */
|
||||
if (propedit) {
|
||||
if (propedit_connected) {
|
||||
/* A) "Connected" - Only those in selected strokes */
|
||||
stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
|
||||
}
|
||||
else {
|
||||
/* B) All points, always */
|
||||
stroke_ok = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* C) Only selected points in selected strokes */
|
||||
stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
|
||||
}
|
||||
|
||||
/* Do stroke... */
|
||||
if (stroke_ok && gps->totpoints) {
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
#if 0 /* XXX: this isn't needed anymore; cannot calculate center this way or propedit breaks */
|
||||
const float ninv = 1.0f / gps->totpoints;
|
||||
float center[3] = {0.0f};
|
||||
|
||||
/* compute midpoint of stroke */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
madd_v3_v3v3fl(center, center, &pt->x, ninv);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* add all necessary points... */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
bool point_ok;
|
||||
|
||||
/* include point? */
|
||||
if (propedit) {
|
||||
/* Always all points in strokes that get included */
|
||||
point_ok = true;
|
||||
}
|
||||
else {
|
||||
/* Only selected points in selected strokes */
|
||||
point_ok = (pt->flag & GP_SPOINT_SELECT) != 0;
|
||||
}
|
||||
|
||||
/* do point... */
|
||||
if (point_ok) {
|
||||
copy_v3_v3(td->iloc, &pt->x);
|
||||
copy_v3_v3(td->center, &pt->x); // XXX: what about t->around == local?
|
||||
|
||||
td->loc = &pt->x;
|
||||
|
||||
td->flag = 0;
|
||||
|
||||
if (pt->flag & GP_SPOINT_SELECT)
|
||||
td->flag |= TD_SELECTED;
|
||||
|
||||
/* configure 2D points so that they don't play up... */
|
||||
if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) {
|
||||
td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
|
||||
// XXX: matrices may need to be different?
|
||||
}
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
unit_m3(td->axismtx); // XXX?
|
||||
|
||||
td++;
|
||||
tail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* March over these points, and calculate the proportional editing distances */
|
||||
if (propedit && (head != tail)) {
|
||||
/* XXX: for now, we are similar enough that this works... */
|
||||
calc_distanceCurveVerts(head, tail - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void createTransData(bContext *C, TransInfo *t)
|
||||
{
|
||||
Scene *scene = t->scene;
|
||||
@@ -7300,6 +7528,16 @@ void createTransData(bContext *C, TransInfo *t)
|
||||
sort_trans_data_dist(t);
|
||||
}
|
||||
}
|
||||
else if (t->options & CTX_GPENCIL_STROKES) {
|
||||
t->flag |= T_POINTS; // XXX...
|
||||
createTransGPencil(C, t);
|
||||
|
||||
if (t->data && (t->flag & T_PROP_EDIT)) {
|
||||
sort_trans_data(t); // makes selected become first in array
|
||||
set_prop_dist(t, 1);
|
||||
sort_trans_data_dist(t);
|
||||
}
|
||||
}
|
||||
else if (t->spacetype == SPACE_IMAGE) {
|
||||
t->flag |= T_POINTS | T_2D_EDIT;
|
||||
if (t->options & CTX_MASK) {
|
||||
|
@@ -961,6 +961,9 @@ void recalcData(TransInfo *t)
|
||||
else if (t->options & CTX_PAINT_CURVE) {
|
||||
flushTransPaintCurve(t);
|
||||
}
|
||||
else if (t->options & CTX_GPENCIL_STROKES) {
|
||||
/* pass? */
|
||||
}
|
||||
else if (t->spacetype == SPACE_IMAGE) {
|
||||
recalcData_image(t);
|
||||
}
|
||||
@@ -1315,6 +1318,9 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
|
||||
if (t->obedit) {
|
||||
t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional);
|
||||
}
|
||||
else if (t->options & CTX_GPENCIL_STROKES) {
|
||||
t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional);
|
||||
}
|
||||
else if (t->options & CTX_MASK) {
|
||||
if (ts->proportional_mask) {
|
||||
t->flag |= T_PROP_EDIT;
|
||||
|
@@ -546,7 +546,11 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (flags & P_GPENCIL_EDIT) {
|
||||
RNA_def_boolean(ot->srna, "gpencil_strokes", 0, "Edit Grease Pencil", "Edit selected Grease Pencil strokes");
|
||||
}
|
||||
|
||||
if ((flags & P_OPTIONS) && !(flags & P_NO_TEXSPACE)) {
|
||||
RNA_def_boolean(ot->srna, "texture_space", 0, "Edit Texture Space", "Edit Object data texture space");
|
||||
prop = RNA_def_boolean(ot->srna, "remove_on_cancel", 0, "Remove on Cancel", "Remove elements on cancel");
|
||||
@@ -581,7 +585,7 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot)
|
||||
|
||||
RNA_def_float_vector_xyz(ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX);
|
||||
|
||||
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS);
|
||||
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | P_GPENCIL_EDIT);
|
||||
}
|
||||
|
||||
static void TRANSFORM_OT_resize(struct wmOperatorType *ot)
|
||||
@@ -601,7 +605,7 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot)
|
||||
|
||||
RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX);
|
||||
|
||||
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS);
|
||||
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS | P_GPENCIL_EDIT);
|
||||
}
|
||||
|
||||
static int skin_resize_poll(bContext *C)
|
||||
@@ -655,7 +659,7 @@ static void TRANSFORM_OT_trackball(struct wmOperatorType *ot)
|
||||
prop = RNA_def_float_vector(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -FLT_MAX, FLT_MAX);
|
||||
RNA_def_property_subtype(prop, PROP_ANGLE);
|
||||
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP);
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT);
|
||||
}
|
||||
|
||||
static void TRANSFORM_OT_rotate(struct wmOperatorType *ot)
|
||||
@@ -678,7 +682,7 @@ static void TRANSFORM_OT_rotate(struct wmOperatorType *ot)
|
||||
prop = RNA_def_float(ot->srna, "value", 0.0f, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2);
|
||||
RNA_def_property_subtype(prop, PROP_ANGLE);
|
||||
|
||||
Transform_Properties(ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP);
|
||||
Transform_Properties(ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_GPENCIL_EDIT);
|
||||
}
|
||||
|
||||
static void TRANSFORM_OT_tilt(struct wmOperatorType *ot)
|
||||
@@ -724,7 +728,7 @@ static void TRANSFORM_OT_bend(struct wmOperatorType *ot)
|
||||
|
||||
RNA_def_float_rotation(ot->srna, "value", 1, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2);
|
||||
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP);
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT);
|
||||
}
|
||||
|
||||
static void TRANSFORM_OT_shear(struct wmOperatorType *ot)
|
||||
@@ -744,7 +748,7 @@ static void TRANSFORM_OT_shear(struct wmOperatorType *ot)
|
||||
|
||||
RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX);
|
||||
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP);
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT);
|
||||
// XXX Shear axis?
|
||||
}
|
||||
|
||||
@@ -806,7 +810,7 @@ static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot)
|
||||
|
||||
RNA_def_float_factor(ot->srna, "value", 0, 0, 1, "Factor", "", 0, 1);
|
||||
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP);
|
||||
Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT);
|
||||
}
|
||||
|
||||
static void TRANSFORM_OT_mirror(struct wmOperatorType *ot)
|
||||
@@ -824,7 +828,7 @@ static void TRANSFORM_OT_mirror(struct wmOperatorType *ot)
|
||||
ot->cancel = transform_cancel;
|
||||
ot->poll = ED_operator_screenactive;
|
||||
|
||||
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL);
|
||||
Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_GPENCIL_EDIT);
|
||||
}
|
||||
|
||||
static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot)
|
||||
|
@@ -541,7 +541,7 @@ typedef enum eDopeSheet_FilterFlag {
|
||||
ADS_FILTER_ONLYNLA = (1 << 2), /* for 'NLA' editor - only include NLA data from AnimData */
|
||||
ADS_FILTER_SELEDIT = (1 << 3), /* for Graph Editor - used to indicate whether to include a filtering flag or not */
|
||||
|
||||
/* general filtering 2 */
|
||||
/* general filtering */
|
||||
ADS_FILTER_SUMMARY = (1 << 4), /* for 'DopeSheet' Editors - include 'summary' line */
|
||||
ADS_FILTER_ONLYOBGROUP = (1 << 5), /* only the objects in the specified object group get used */
|
||||
|
||||
@@ -564,6 +564,8 @@ typedef enum eDopeSheet_FilterFlag {
|
||||
ADS_FILTER_NOSPK = (1 << 21),
|
||||
ADS_FILTER_NOLINESTYLE = (1 << 22),
|
||||
ADS_FILTER_NOMODIFIERS = (1 << 23),
|
||||
ADS_FILTER_NOGPENCIL = (1 << 24),
|
||||
/* NOTE: all new datablock filters will have to go in filterflag2 (see below) */
|
||||
|
||||
/* NLA-specific filters */
|
||||
ADS_FILTER_NLA_NOACT = (1 << 25), /* if the AnimData block has no NLA data, don't include to just show Action-line */
|
||||
@@ -581,6 +583,8 @@ typedef enum eDopeSheet_FilterFlag {
|
||||
typedef enum eDopeSheet_Flag {
|
||||
ADS_FLAG_SUMMARY_COLLAPSED = (1 << 0), /* when summary is shown, it is collapsed, so all other channels get hidden */
|
||||
ADS_FLAG_SHOW_DBFILTERS = (1 << 1) /* show filters for datablocks */
|
||||
|
||||
/* NOTE: datablock filter flags continued (1 << 10) onwards... */
|
||||
} eDopeSheet_Flag;
|
||||
|
||||
|
||||
|
@@ -33,6 +33,9 @@
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_ID.h"
|
||||
|
||||
struct AnimData;
|
||||
|
||||
|
||||
/* Grease-Pencil Annotations - 'Stroke Point'
|
||||
* -> Coordinates may either be 2d or 3d depending on settings at the time
|
||||
* -> Coordinates of point on stroke, in proportions of window size
|
||||
@@ -42,8 +45,15 @@ typedef struct bGPDspoint {
|
||||
float x, y, z; /* co-ordinates of point (usually 2d, but can be 3d as well) */
|
||||
float pressure; /* pressure of input device (from 0 to 1) at this point */
|
||||
float time; /* seconds since start of stroke */
|
||||
int flag; /* additional options (NOTE: can shrink this field down later if needed) */
|
||||
} bGPDspoint;
|
||||
|
||||
/* bGPDspoint->flag */
|
||||
typedef enum eGPDspoint_Flag {
|
||||
/* stroke point is selected (for editing) */
|
||||
GP_SPOINT_SELECT = (1 << 0)
|
||||
} eGPSPoint_Flag;
|
||||
|
||||
/* Grease-Pencil Annotations - 'Stroke'
|
||||
* -> A stroke represents a (simplified version) of the curve
|
||||
* drawn by the user in one 'mousedown'->'mouseup' operation
|
||||
@@ -61,15 +71,18 @@ typedef struct bGPDstroke {
|
||||
} bGPDstroke;
|
||||
|
||||
/* bGPDstroke->flag */
|
||||
typedef enum eGPDstroke_Flag {
|
||||
/* stroke is in 3d-space */
|
||||
#define GP_STROKE_3DSPACE (1<<0)
|
||||
GP_STROKE_3DSPACE = (1 << 0),
|
||||
/* stroke is in 2d-space */
|
||||
#define GP_STROKE_2DSPACE (1<<1)
|
||||
GP_STROKE_2DSPACE = (1 << 1),
|
||||
/* stroke is in 2d-space (but with special 'image' scaling) */
|
||||
#define GP_STROKE_2DIMAGE (1<<2)
|
||||
GP_STROKE_2DIMAGE = (1 << 2),
|
||||
/* stroke is selected */
|
||||
GP_STROKE_SELECT = (1 << 3),
|
||||
/* only for use with stroke-buffer (while drawing eraser) */
|
||||
#define GP_STROKE_ERASER (1<<15)
|
||||
|
||||
GP_STROKE_ERASER = (1 << 15)
|
||||
} eGPDstroke_Flag;
|
||||
|
||||
/* Grease-Pencil Annotations - 'Frame'
|
||||
* -> Acts as storage for the 'image' formed by strokes
|
||||
@@ -80,15 +93,18 @@ typedef struct bGPDframe {
|
||||
ListBase strokes; /* list of the simplified 'strokes' that make up the frame's data */
|
||||
|
||||
int framenum; /* frame number of this frame */
|
||||
int flag; /* temp settings */
|
||||
|
||||
short flag; /* temp settings */
|
||||
short key_type; /* keyframe type (eBezTriple_KeyframeType) */
|
||||
} bGPDframe;
|
||||
|
||||
/* bGPDframe->flag */
|
||||
/* bGPDframe->flag */
|
||||
typedef enum eGPDframe_Flag {
|
||||
/* frame is being painted on */
|
||||
#define GP_FRAME_PAINT (1<<0)
|
||||
GP_FRAME_PAINT = (1 << 0),
|
||||
/* for editing in Action Editor */
|
||||
#define GP_FRAME_SELECT (1<<1)
|
||||
|
||||
GP_FRAME_SELECT = (1 << 1)
|
||||
} eGPDframe_Flag;
|
||||
|
||||
/* Grease-Pencil Annotations - 'Layer' */
|
||||
typedef struct bGPDlayer {
|
||||
@@ -97,38 +113,52 @@ typedef struct bGPDlayer {
|
||||
ListBase frames; /* list of annotations to display for frames (bGPDframe list) */
|
||||
bGPDframe *actframe; /* active frame (should be the frame that is currently being displayed) */
|
||||
|
||||
int flag; /* settings for layer */
|
||||
short flag; /* settings for layer */
|
||||
short thickness; /* current thickness to apply to strokes */
|
||||
short gstep; /* max number of frames between active and ghost to show (0=only those on either side) */
|
||||
|
||||
short gstep; /* Ghosts Before: max number of ghost frames to show between active frame and the one before it (0 = only the ghost itself) */
|
||||
short gstep_next; /* Ghosts After: max number of ghost frames to show after active frame and the following it (0 = only the ghost itself) */
|
||||
|
||||
float gcolor_prev[3]; /* optional color for ghosts before the active frame */
|
||||
float gcolor_next[3]; /* optional color for ghosts after the active frame */
|
||||
|
||||
float color[4]; /* color that should be used to draw all the strokes in this layer */
|
||||
float fill[4]; /* color that should be used for drawing "fills" for strokes */
|
||||
|
||||
char info[128]; /* optional reference info about this layer (i.e. "director's comments, 12/3")
|
||||
* this is used for the name of the layer too and kept unique. */
|
||||
} bGPDlayer;
|
||||
|
||||
/* bGPDlayer->flag */
|
||||
typedef enum eGPDlayer_Flag {
|
||||
/* don't display layer */
|
||||
#define GP_LAYER_HIDE (1<<0)
|
||||
GP_LAYER_HIDE = (1 << 0),
|
||||
/* protected from further editing */
|
||||
#define GP_LAYER_LOCKED (1<<1)
|
||||
GP_LAYER_LOCKED = (1 << 1),
|
||||
/* layer is 'active' layer being edited */
|
||||
#define GP_LAYER_ACTIVE (1<<2)
|
||||
GP_LAYER_ACTIVE = (1 << 2),
|
||||
/* draw points of stroke for debugging purposes */
|
||||
#define GP_LAYER_DRAWDEBUG (1<<3)
|
||||
/* do onionskinning */
|
||||
#define GP_LAYER_ONIONSKIN (1<<4)
|
||||
GP_LAYER_DRAWDEBUG = (1 << 3),
|
||||
/* do onion skinning */
|
||||
GP_LAYER_ONIONSKIN = (1 << 4),
|
||||
/* for editing in Action Editor */
|
||||
#define GP_LAYER_SELECT (1<<5)
|
||||
GP_LAYER_SELECT = (1 << 5),
|
||||
/* current frame for layer can't be changed */
|
||||
#define GP_LAYER_FRAMELOCK (1<<6)
|
||||
GP_LAYER_FRAMELOCK = (1 << 6),
|
||||
/* don't render xray (which is default) */
|
||||
#define GP_LAYER_NO_XRAY (1<<7)
|
||||
|
||||
GP_LAYER_NO_XRAY = (1 << 7),
|
||||
/* use custom color for ghosts before current frame */
|
||||
GP_LAYER_GHOST_PREVCOL = (1 << 8),
|
||||
/* use custom color for ghosts after current frame */
|
||||
GP_LAYER_GHOST_NEXTCOL = (1 << 9),
|
||||
/* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */
|
||||
GP_LAYER_VOLUMETRIC = (1 << 10),
|
||||
} eGPDlayer_Flag;
|
||||
|
||||
/* Grease-Pencil Annotations - 'DataBlock' */
|
||||
typedef struct bGPdata {
|
||||
ID id; /* Grease Pencil data is */
|
||||
ID id; /* Grease Pencil data is a datablock */
|
||||
struct AnimData *adt; /* animation data - for animating draw settings */
|
||||
|
||||
/* saved Grease-Pencil data */
|
||||
ListBase layers; /* bGPDlayers */
|
||||
@@ -144,23 +174,33 @@ typedef struct bGPdata {
|
||||
} bGPdata;
|
||||
|
||||
/* bGPdata->flag */
|
||||
// XXX many of these flags should be deprecated for more general ideas in 2.5
|
||||
/* NOTE: A few flags have been deprecated since early 2.5,
|
||||
* since they have been made redundant by interaction
|
||||
* changes made during the porting process.
|
||||
*/
|
||||
typedef enum eGPdata_Flag {
|
||||
/* don't allow painting to occur at all */
|
||||
// XXX is deprecated - not well understood
|
||||
// #define GP_DATA_LMBPLOCK (1<<0)
|
||||
/* GP_DATA_LMBPLOCK = (1 << 0), */
|
||||
|
||||
/* show debugging info in viewport (i.e. status print) */
|
||||
#define GP_DATA_DISPINFO (1<<1)
|
||||
GP_DATA_DISPINFO = (1 << 1),
|
||||
/* in Action Editor, show as expanded channel */
|
||||
#define GP_DATA_EXPAND (1<<2)
|
||||
GP_DATA_EXPAND = (1 << 2),
|
||||
|
||||
/* is the block overriding all clicks? */
|
||||
// XXX is deprecated - nasty old concept
|
||||
// #define GP_DATA_EDITPAINT (1<<3)
|
||||
/* GP_DATA_EDITPAINT = (1 << 3), */
|
||||
|
||||
/* new strokes are added in viewport space */
|
||||
#define GP_DATA_VIEWALIGN (1<<4)
|
||||
/* Project into the screens Z values */
|
||||
#define GP_DATA_DEPTH_VIEW (1<<5)
|
||||
#define GP_DATA_DEPTH_STROKE (1<<6)
|
||||
GP_DATA_VIEWALIGN = (1 << 4),
|
||||
|
||||
/* Project into the screen's Z values */
|
||||
GP_DATA_DEPTH_VIEW = (1 << 5),
|
||||
GP_DATA_DEPTH_STROKE = (1 << 6),
|
||||
|
||||
#define GP_DATA_DEPTH_STROKE_ENDPOINTS (1<<7)
|
||||
GP_DATA_DEPTH_STROKE_ENDPOINTS = (1 << 7),
|
||||
|
||||
/* Stroke Editing Mode - Toggle to enable alternative keymap for easier editing of stroke points */
|
||||
GP_DATA_STROKE_EDITMODE = (1 << 8)
|
||||
} eGPdata_Flag;
|
||||
|
||||
#endif /* __DNA_GPENCIL_TYPES_H__ */
|
||||
|
@@ -462,6 +462,12 @@ static void rna_def_dopesheet(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Display Speaker", "Include visualization of speaker related animation data");
|
||||
RNA_def_property_ui_icon(prop, ICON_SPEAKER, 0);
|
||||
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "show_gpencil", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag", ADS_FILTER_NOGPENCIL);
|
||||
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);
|
||||
}
|
||||
|
||||
static void rna_def_action_group(BlenderRNA *brna)
|
||||
|
@@ -44,6 +44,8 @@
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "BKE_gpencil.h"
|
||||
@@ -53,6 +55,16 @@ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe
|
||||
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
|
||||
}
|
||||
|
||||
static char *rna_GPencilLayer_path(PointerRNA *ptr)
|
||||
{
|
||||
bGPDlayer *gpl = (bGPDlayer *)ptr->data;
|
||||
char name_esc[sizeof(gpl->info) * 2];
|
||||
|
||||
BLI_strescape(name_esc, gpl->info, sizeof(name_esc));
|
||||
|
||||
return BLI_sprintfN("layers[\"%s\"]", name_esc);
|
||||
}
|
||||
|
||||
static int rna_GPencilLayer_active_frame_editable(PointerRNA *ptr)
|
||||
{
|
||||
bGPDlayer *gpl = (bGPDlayer *)ptr->data;
|
||||
@@ -64,9 +76,36 @@ static int rna_GPencilLayer_active_frame_editable(PointerRNA *ptr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rna_GPencilLayer_line_width_range(PointerRNA *ptr, int *min, int *max,
|
||||
int *softmin, int *softmax)
|
||||
{
|
||||
bGPDlayer *gpl = ptr->data;
|
||||
|
||||
/* The restrictions on max width here are due to OpenGL on Windows not supporting
|
||||
* any widths greater than 10 (for driver-drawn) strokes/points.
|
||||
*
|
||||
* Although most of our 2D strokes also don't suffer from this restriction,
|
||||
* it's relatively hard to test for that. So, for now, only volumetric strokes
|
||||
* get to be larger...
|
||||
*/
|
||||
if (gpl->flag & GP_LAYER_VOLUMETRIC) {
|
||||
*min = 1;
|
||||
*max = 300;
|
||||
|
||||
*softmin = 1;
|
||||
*softmax = 100;
|
||||
}
|
||||
else {
|
||||
*min = 1;
|
||||
*max = 10;
|
||||
|
||||
*softmin = 1;
|
||||
*softmax = 10;
|
||||
}
|
||||
}
|
||||
|
||||
static PointerRNA rna_GPencil_active_layer_get(PointerRNA *ptr)
|
||||
{
|
||||
|
||||
bGPdata *gpd = ptr->id.data;
|
||||
|
||||
if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */
|
||||
@@ -106,6 +145,33 @@ static void rna_GPencil_active_layer_set(PointerRNA *ptr, PointerRNA value)
|
||||
}
|
||||
}
|
||||
|
||||
static int rna_GPencil_active_layer_index_get(PointerRNA *ptr)
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)ptr->id.data;
|
||||
bGPDlayer *gpl = gpencil_layer_getactive(gpd);
|
||||
|
||||
return BLI_findindex(&gpd->layers, gpl);
|
||||
}
|
||||
|
||||
static void rna_GPencil_active_layer_index_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)ptr->id.data;
|
||||
bGPDlayer *gpl = BLI_findlink(&gpd->layers, value);
|
||||
|
||||
gpencil_layer_setactive(gpd, gpl);
|
||||
}
|
||||
|
||||
static void rna_GPencil_active_layer_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)ptr->id.data;
|
||||
|
||||
*min = 0;
|
||||
*max = max_ii(0, BLI_listbase_count(&gpd->layers) - 1);
|
||||
|
||||
*softmin = *min;
|
||||
*softmax = *max;
|
||||
}
|
||||
|
||||
static void rna_GPencilLayer_info_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
bGPdata *gpd = ptr->id.data;
|
||||
@@ -117,6 +183,63 @@ 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 bGPDstroke *rna_GPencil_stroke_point_find_stroke(const bGPdata *gpd, const bGPDspoint *pt, bGPDlayer **r_gpl, bGPDframe **r_gpf)
|
||||
{
|
||||
bGPDlayer *gpl;
|
||||
bGPDstroke *gps;
|
||||
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, gpd, pt)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (r_gpl) *r_gpl = NULL;
|
||||
if (r_gpf) *r_gpf = NULL;
|
||||
|
||||
/* there's no faster alternative than just looping over everything... */
|
||||
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
if (gpl->actframe) {
|
||||
for (gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
|
||||
if ((pt >= gps->points) && (pt < &gps->points[gps->totpoints])) {
|
||||
/* found it */
|
||||
if (r_gpl) *r_gpl = gpl;
|
||||
if (r_gpf) *r_gpf = gpl->actframe;
|
||||
|
||||
return gps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* didn't find it */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rna_GPencil_stroke_point_select_set(PointerRNA *ptr, const int value)
|
||||
{
|
||||
bGPdata *gpd = ptr->id.data;
|
||||
bGPDspoint *pt = ptr->data;
|
||||
bGPDstroke *gps = NULL;
|
||||
|
||||
/* Ensure that corresponding stroke is set
|
||||
* - Since we don't have direct access, we're going to have to search
|
||||
* - We don't apply selection value unless we can find the corresponding
|
||||
* stroke, so that they don't get out of sync
|
||||
*/
|
||||
gps = rna_GPencil_stroke_point_find_stroke(gpd, pt, NULL, NULL);
|
||||
if (gps) {
|
||||
/* Set the new selection state for the point */
|
||||
if (value)
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
else
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
|
||||
/* Check if the stroke should be selected or not... */
|
||||
gpencil_stroke_sync_selection(gps);
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_GPencil_stroke_point_add(bGPDstroke *stroke, int count)
|
||||
{
|
||||
if (count > 0) {
|
||||
@@ -180,6 +303,27 @@ static void rna_GPencil_stroke_remove(bGPDframe *frame, ReportList *reports, Poi
|
||||
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
|
||||
}
|
||||
|
||||
static void rna_GPencil_stroke_select_set(PointerRNA *ptr, const int value)
|
||||
{
|
||||
bGPDstroke *gps = ptr->data;
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
|
||||
/* set new value */
|
||||
if (value)
|
||||
gps->flag |= GP_STROKE_SELECT;
|
||||
else
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
|
||||
/* ensure that the stroke's points are selected in the same way */
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
if (value)
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
else
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
static bGPDframe *rna_GPencil_frame_new(bGPDlayer *layer, ReportList *reports, int frame_number)
|
||||
{
|
||||
bGPDframe *frame;
|
||||
@@ -291,6 +435,12 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna)
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_ui_text(prop, "Pressure", "Pressure of tablet at point when drawing it");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SPOINT_SELECT);
|
||||
RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_point_select_set");
|
||||
RNA_def_property_ui_text(prop, "Select", "Point is selected for viewport editing");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
}
|
||||
|
||||
static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
@@ -338,12 +488,19 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
|
||||
RNA_def_property_struct_type(prop, "GPencilStrokePoint");
|
||||
RNA_def_property_ui_text(prop, "Stroke Points", "Stroke data points");
|
||||
rna_def_gpencil_stroke_points_api(brna, prop);
|
||||
|
||||
|
||||
/* Settings */
|
||||
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, stroke_draw_mode_items);
|
||||
RNA_def_property_ui_text(prop, "Draw Mode", "");
|
||||
RNA_def_property_update(prop, 0, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STROKE_SELECT);
|
||||
RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_select_set");
|
||||
RNA_def_property_ui_text(prop, "Select", "Stroke is selected for viewport editing");
|
||||
RNA_def_property_update(prop, 0, "rna_GPencil_update");
|
||||
}
|
||||
|
||||
static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
@@ -455,6 +612,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
srna = RNA_def_struct(brna, "GPencilLayer", NULL);
|
||||
RNA_def_struct_sdna(srna, "bGPDlayer");
|
||||
RNA_def_struct_ui_text(srna, "Grease Pencil Layer", "Collection of related sketches");
|
||||
RNA_def_struct_path_func(srna, "rna_GPencilLayer_path");
|
||||
|
||||
/* Name */
|
||||
prop = RNA_def_property(srna, "info", PROP_STRING, PROP_NONE);
|
||||
@@ -477,7 +635,14 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
RNA_def_property_editable_func(prop, "rna_GPencilLayer_active_frame_editable");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
/* Drawing Color */
|
||||
/* Draw Style */
|
||||
// TODO: replace these with a "draw type" combo (i.e. strokes only, filled strokes, strokes + fills, volumetric)?
|
||||
prop = RNA_def_property(srna, "use_volumetric_strokes", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_VOLUMETRIC);
|
||||
RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in a volumetric effect");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* Stroke Drawing Color */
|
||||
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
@@ -486,14 +651,29 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
|
||||
prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "color[3]");
|
||||
RNA_def_property_range(prop, 0.3, 1.0f);
|
||||
RNA_def_property_range(prop, 0.0, 1.0f);
|
||||
RNA_def_property_ui_text(prop, "Opacity", "Layer Opacity");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* Fill Drawing Color */
|
||||
prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, NULL, "fill");
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "fill[3]");
|
||||
RNA_def_property_range(prop, 0.0, 1.0f);
|
||||
RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* Line Thickness */
|
||||
prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL);
|
||||
RNA_def_property_int_sdna(prop, NULL, "thickness");
|
||||
RNA_def_property_range(prop, 1, 10);
|
||||
//RNA_def_property_range(prop, 1, 10); /* 10 px limit comes from Windows OpenGL limits for natively-drawn strokes */
|
||||
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_GPencilLayer_line_width_range");
|
||||
RNA_def_property_ui_text(prop, "Thickness", "Thickness of strokes (in pixels)");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
@@ -503,29 +683,59 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
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");
|
||||
|
||||
prop = RNA_def_property(srna, "ghost_range_max", PROP_INT, PROP_NONE);
|
||||
prop = RNA_def_property(srna, "ghost_before_range", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "gstep");
|
||||
RNA_def_property_range(prop, 0, 120);
|
||||
RNA_def_property_ui_text(prop, "Max Ghost Range",
|
||||
"Maximum number of frames on either side of the active frame to show "
|
||||
"(0 = show the 'first' available sketch on either side)");
|
||||
RNA_def_property_ui_text(prop, "Frames Before",
|
||||
"Maximum number of frames to show before current frame "
|
||||
"(0 = show only the previous sketch)");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "ghost_after_range", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "gstep_next");
|
||||
RNA_def_property_range(prop, 0, 120);
|
||||
RNA_def_property_ui_text(prop, "Frames After",
|
||||
"Maximum number of frames to show after current frame "
|
||||
"(0 = show only the next sketch)");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_ghost_custom_colors", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_GHOST_PREVCOL | GP_LAYER_GHOST_NEXTCOL);
|
||||
RNA_def_property_ui_text(prop, "Use Custom Ghost Colors", "Use custom colors for ghost frames");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "before_color", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, NULL, "gcolor_prev");
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_ui_text(prop, "Before Color", "Base color for ghosts before the active frame");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "after_color", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, NULL, "gcolor_next");
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* Flags */
|
||||
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE);
|
||||
RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, 1);
|
||||
RNA_def_property_ui_text(prop, "Hide", "Set layer Visibility");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_LOCKED);
|
||||
RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1);
|
||||
RNA_def_property_ui_text(prop, "Locked", "Protect layer from further editing and/or frame changes");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "lock_frame", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_FRAMELOCK);
|
||||
RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1);
|
||||
RNA_def_property_ui_text(prop, "Frame Locked", "Lock current frame displayed by layer");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* expose as layers.active */
|
||||
#if 0
|
||||
@@ -539,7 +749,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_SELECT);
|
||||
RNA_def_property_ui_text(prop, "Select", "Layer is selected for editing in the Dope Sheet");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* XXX keep this option? */
|
||||
prop = RNA_def_property(srna, "show_points", PROP_BOOLEAN, PROP_NONE);
|
||||
@@ -592,6 +802,14 @@ static void rna_def_gpencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
RNA_def_property_pointer_funcs(prop, "rna_GPencil_active_layer_get", "rna_GPencil_active_layer_set", NULL, NULL);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Active Layer", "Active grease pencil layer");
|
||||
|
||||
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
|
||||
|
||||
RNA_def_property_int_funcs(prop,
|
||||
"rna_GPencil_active_layer_index_get",
|
||||
"rna_GPencil_active_layer_index_set",
|
||||
"rna_GPencil_active_layer_index_range");
|
||||
RNA_def_property_ui_text(prop, "Active Layer Index", "Index of active grease pencil layer");
|
||||
}
|
||||
|
||||
static void rna_def_gpencil_data(BlenderRNA *brna)
|
||||
@@ -620,6 +838,9 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Layers", "");
|
||||
rna_def_gpencil_layers_api(brna, prop);
|
||||
|
||||
/* Animation Data */
|
||||
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");
|
||||
@@ -631,7 +852,13 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
|
||||
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");
|
||||
|
||||
/* API Functions */
|
||||
func = RNA_def_function(srna, "clear", "rna_GPencil_clear");
|
||||
RNA_def_function_ui_description(func, "Remove all the grease pencil data");
|
||||
}
|
||||
|
@@ -317,8 +317,8 @@ static void rna_def_movieclip(BlenderRNA *brna)
|
||||
/* grease pencil */
|
||||
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencil");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this movie clip");
|
||||
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
|
||||
|
||||
|
@@ -7572,8 +7572,8 @@ static void rna_def_nodetree(BlenderRNA *brna)
|
||||
/* Grease Pencil */
|
||||
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencil");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil datablock");
|
||||
RNA_def_property_update(prop, NC_NODE, NULL);
|
||||
|
||||
|
@@ -2729,16 +2729,16 @@ static void rna_def_object(BlenderRNA *brna)
|
||||
/* Grease Pencil */
|
||||
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencil");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil datablock");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
|
||||
|
||||
/* pose */
|
||||
prop = RNA_def_property(srna, "pose_library", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "poselib");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "Action");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Pose Library", "Action used as a pose library for armatures");
|
||||
|
||||
prop = RNA_def_property(srna, "pose", PROP_POINTER, PROP_NONE);
|
||||
|
@@ -5738,8 +5738,8 @@ void RNA_def_scene(BlenderRNA *brna)
|
||||
/* Grease Pencil */
|
||||
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencil");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil datablock");
|
||||
RNA_def_property_update(prop, NC_SCENE, NULL);
|
||||
|
||||
|
@@ -2430,8 +2430,8 @@ static void rna_def_space_image(BlenderRNA *brna)
|
||||
/* grease pencil */
|
||||
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencil");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this space");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
|
||||
|
||||
@@ -2615,8 +2615,8 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
|
||||
/* grease pencil */
|
||||
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencil");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this space");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
|
||||
|
||||
|
@@ -1435,8 +1435,8 @@ static void rna_def_trackingTrack(BlenderRNA *brna)
|
||||
/* grease pencil */
|
||||
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "gpd");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "GreasePencil");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this track");
|
||||
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
|
||||
|
||||
|
@@ -324,6 +324,9 @@ typedef struct wmNotifier {
|
||||
#define ND_NLA_ACTCHANGE (74<<16)
|
||||
#define ND_FCURVES_ORDER (75<<16)
|
||||
|
||||
/* NC_GPENCIL */
|
||||
#define ND_GPENCIL_EDITMODE (85<<16)
|
||||
|
||||
/* NC_GEOM Geometry */
|
||||
/* Mesh, Curve, MetaBall, Armature, .. */
|
||||
#define ND_SELECT (90<<16)
|
||||
|
@@ -4801,6 +4801,7 @@ static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
|
||||
WM_modalkeymap_assign(keymap, "CLIP_OT_select_circle");
|
||||
WM_modalkeymap_assign(keymap, "MASK_OT_select_circle");
|
||||
WM_modalkeymap_assign(keymap, "NODE_OT_select_circle");
|
||||
WM_modalkeymap_assign(keymap, "GPENCIL_OT_select_circle");
|
||||
|
||||
}
|
||||
|
||||
@@ -4897,6 +4898,7 @@ static void gesture_border_modal_keymap(wmKeyConfig *keyconf)
|
||||
WM_modalkeymap_assign(keymap, "VIEW3D_OT_select_border");
|
||||
WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom_border"); /* XXX TODO: zoom border should perhaps map rightmouse to zoom out instead of in+cancel */
|
||||
WM_modalkeymap_assign(keymap, "IMAGE_OT_render_border");
|
||||
WM_modalkeymap_assign(keymap, "GPENCIL_OT_select_border");
|
||||
}
|
||||
|
||||
/* zoom to border modal operators */
|
||||
|
Reference in New Issue
Block a user