Grease Pencil - Storyboarding Features (merge from GPencil_EditStrokes branch)

This merge-commit brings in a number of new features and workflow/UI improvements for
working with Grease Pencil. While these were originally targetted at improving
the workflow for creating 3D storyboards in Blender using the Grease Pencil,
many of these changes should also prove useful in other workflows too.

The main highlights here are:
1) It is now possible to edit Grease Pencil strokes
   - Use D Tab, or toggle the "Enable Editing" toggles in the Toolbar/Properties regions
     to enter "Stroke Edit Mode". In this mode, many common editing tools will
     operate on Grease Pencil stroke points instead.
   - Tools implemented include Select, Select All/Border/Circle/Linked/More/Less,
     Grab, Rotate, Scale, Bend, Shear, To Sphere, Mirror, Duplicate, Delete.
   - Proportional Editing works when using the transform tools

2) Grease Pencil stroke settings can now be animated
   NOTE: Currently drivers don't work, but if time allows, this may still be
         added before the release.

3) Strokes can be drawn with "filled" interiors, using a separate set of
   colour/opacity settings to the ones used for the lines themselves.

   This makes use of OpenGL filled polys, which has the limitation of only
   being able to fill convex shapes. Some artifacts may be visible on concave
   shapes (e.g. pacman's mouth will be overdrawn)

4) "Volumetric Strokes" - An alternative drawing technique for stroke drawing
   has been added which draws strokes as a series of screen-aligned discs.

   While this was originally a partial experimental technique at getting better
   quality 3D lines, the effects possible using this technique were interesting
   enough to warrant making this a dedicated feature. Best results when partial
   opacity and large stroke widths are used.

5) Improved Onion Skinning Support
   - Different colours can be selected for the before/after ghosts. To do so,
     enable the "colour wheel" toggle beside the Onion Skinning toggle, and set
     the colours accordingly.
   - Different numbers of ghosts can be shown before/after the current frame

6) Grease Pencil datablocks are now attached to the scene by default instead of
   the active object.
   - For a long time, the object-attachment has proved to be quite problematic
     for users to keep track of. Now that this is done at scene level, it is
     easier for most users to use.
   - An exception for old files (and for any addons which may benefit from object
     attachment instead), is that if the active object has a Grease Pencil datablock,
     that will be used instead.
   - It is not currently possible to choose object-attachment from the UI, but
     it is simple to do this from the console instead, by doing:
     context.active_object.grease_pencil = bpy.data.grease_pencil["blah"]

7) Various UI Cleanups
   - The layers UI has been cleaned up to use a list instead of the nested-panels
     design. Apart from saving space, this is also much nicer to look at now.

   - The UI code is now all defined in Python. To support this, it has been necessary
     to add some new context properties to make it easier to access these settings.
     e.g. "gpencil_data" for the datablock
          "active_gpencil_layer" and "active_gpencil_frame" for active data,
          "editable_gpencil_strokes" for the strokes that can be edited

   - The "stroke placement/alignment" settings (previously "Drawing Settings" at the
     bottom of the Grease Pencil panel in the Properties Region) is now located in
     the toolbar. These were more toolsettings than properties for how GPencil got drawn.

   - "Use Sketching Sessions" has been renamed "Continuous Drawing", as per a
     suggestion for an earlier discussion on developer.blender.org

   - By default, the painting operator will wait for a mouse button to be pressed
     before it starts creating the stroke. This is to make it easier to include
     this operator in various toolbars/menus/etc.   To get it immediately starting
     (as when you hold down DKEy to draw), set "wait_for_input" to False.

   - GPencil Layers can be rearranged in the "Grease Pencil" mode of the Action Editor

   - Toolbar panels have been added to all the other editors which support these.

8) Pie menus for quick-access to tools
   A set of experimental pie menus has been included for quick access to many
   tools and settings. It is not necessary to use these to get things done,
   but they have been designed to help make certain common tasks easier.

   - Ctrl-D = The main pie menu. Reveals tools in a context sensitive and
              spatially stable manner.
   - D Q    = "Quick Settings" pie. This allows quick access to the active
              layer's settings. Notably, colours, thickness, and turning
              onion skinning on/off.
This commit is contained in:
Joshua Leung 2014-12-01 01:52:06 +13:00
parent 46c80d5d11
commit 14b951747f
69 changed files with 4359 additions and 395 deletions

View File

@ -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', []),

View File

@ -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)

View File

@ -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"

View File

@ -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")

View File

@ -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.

View File

@ -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

View File

@ -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__)

View File

@ -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'

View File

@ -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!

View File

@ -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

View File

@ -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[]);

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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... */

View File

@ -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:

View File

@ -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;

View File

@ -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;

View File

@ -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
)

View File

@ -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);
}
}
}

View File

@ -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 */

View File

@ -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... */

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}
/* ****************************************** */

View File

@ -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");
}

View 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);
}
/* ********************************************** */

View File

@ -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);

View 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;
}
}
}
/* ******************************************************** */

View File

@ -159,6 +159,7 @@ typedef enum eAnim_ChannelType {
ANIMTYPE_DSLAT,
ANIMTYPE_DSLINESTYLE,
ANIMTYPE_DSSPK,
ANIMTYPE_DSGPENCIL,
ANIMTYPE_SHAPEKEY,

View File

@ -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

View File

@ -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 ---------------- */

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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();

View File

@ -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 ************************/

View File

@ -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:

View File

@ -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))

View File

@ -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) {

View File

@ -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) {

View File

@ -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))

View File

@ -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;
}
}

View File

@ -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 ************* */

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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");

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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__ */

View File

@ -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)

View File

@ -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");
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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 */