1
1

Compare commits

...

106 Commits

Author SHA1 Message Date
027824714e Code Cleanup: Remove some redundant operations 2014-10-31 19:17:46 +13:00
dfc7423a26 Code Cleanup: bool in place of short 2014-10-31 18:51:05 +13:00
e33691dc59 Fix: GP Stroke verts are now properly masked behind geometry now when X-Ray option is disabled 2014-10-31 14:46:20 +13:00
f193652814 Code Cleanup: Deduplicate logic for calculating 2D stroke coordinates for drawing 2014-10-31 14:38:48 +13:00
d404a45df0 GPencil Drawing: Support for drawing filled strokes
It is now possible for the insides (i.e. the region contained within the stroke)
Grease Pencil strokes to be drawn "filled". This initial implementation is quite
crude.

Main Limitations:
- It uses OpenGL polygons for rendering. The main thing to be aware of with this
  is that it *DOES NOT HANDLE CONCAVE* strokes too well. That is, it will just
  plaster over the concave regions, which may not be what you really want

- Fills are currently turned on/off using the opacity settings. Whether we continue
  doing this remains to be seen...
2014-10-31 01:43:59 +13:00
7a80a76b0d RNA: Line width for volumetric strokes is allowed to be higher
While 3D strokes currently have a size limitation, this doesn't apply to volumetrics.

TODO
Most of our 2D strokes don't have a limitation either, but we can't easily
identify such cases in this function, so for now the limit holds.
2014-10-30 19:38:38 +13:00
24380454da Code cleanup: renamed function for drawing 2D strokes to make more sense now 2014-10-30 01:38:19 +13:00
365b38799a Volumetric strokes draw in 2D views too now 2014-10-30 01:37:50 +13:00
f11a5ce8c1 Make volumetric stroke drawing a per-layer option 2014-10-30 01:18:00 +13:00
37c08c1e9f Fix: Poll for toolshelf UI buttons was spitting out warnings
Casting to bool is safer there
2014-10-28 15:39:30 +13:00
32db28d08a Code cleanup: Tidy up some stuff from previous commit 2014-10-28 15:37:15 +13:00
cf3ce9fbfc Volumetric strokes are also drawn during stroke drawing too
To make it easier to see the final effect of volumetric strokes, these are now
also drawn when painting new strokes
2014-10-28 01:09:03 +13:00
0a0c2d48d5 Fix for memory leak in previous commit 2014-10-27 23:56:03 +13:00
ee89e770e3 Proper implementation for "Volumetric Strokes" drawing
Due to the enthusiastic reaction from the community, I've decided to make this
a proper drawing technique. This commit separates the drawing code for this
out into its own method, and reduces the amount of costly GL read/write calls used
by performing more of this using Blender's internal math library. As a result,
it seems (at least in my limited tests) to be running much faster :)

TODO:
- Configuration options for controlling when this is used in combination
  with the other drawing techniques
- Use for stroke drawing too when enabled?
- Allow setting stroke sizes to be much thicker (and also set a better base-size)
2014-10-27 12:58:52 +13:00
3eca27cdd1 Hack: Quadrics-based drawing technique for 3D strokes
This is really really hacky stuff. I've literally just dumped this code in there
without any attempt at cleaning it up or properly integrating it, as it was just
a quick test which turned out to provide some rather interesting results!
2014-10-25 02:08:49 +13:00
527d1593e8 UI and properties support for having "Filled" GPencil Strokes/Shapes 2014-10-24 01:04:15 +13:00
79236d5b81 GPencil Draw Operator: Implemented proper method to delay start of drawing
When running GPencil drawing operators from the toolbar or from a (pie) menu,
drawing operations shouldn't start immediately, but rather, only when users
actually click to start drawing. This is because the mouse is often not in a
suitable location for this to work when the operators get invoked from such
UI controls.

Previously, we used a hack which just detected certain types of events. This worked
ok for toolbars, but not for menus. Now, we've got a proper property to enable this
behaviour instead.
2014-10-23 23:03:43 +13:00
fa9a41cd02 Expose Grease Pencil "Convert to Curves" operator directly in toolbar instead
It only really applies to the 3D view, so this makes more sense, and fits
the workflows for modellers and mograph artists more.
2014-10-23 19:35:57 +13:00
86749a5f7c Tweaks for Delete Active Frame in GPencil UI
* Expose "delete active frame" directly in GP UI (alongside frame locking)
  instead of hiding in a submenu
* Deleting the active frame immediately makes the one before it active
2014-10-23 19:30:41 +13:00
23bbab6bdc Bugfix: Fix for potentially uninitialised matrix, and 2D eraser being broken following recent changes 2014-10-23 17:58:24 +13:00
f01a10d07d GPencil: Refactor out space conversion APIs in response to Sergey's changes 2014-10-23 17:50:14 +13:00
c80972286c Merge branch 'master' into GPencil_EditStrokes
Conflicts (Unresolved):
	source/blender/editors/gpencil/gpencil_paint.c
2014-10-23 16:22:22 +13:00
1219070c55 Code Cleanup - Remove dead code 2014-10-23 16:16:35 +13:00
aadb8305b4 Border Select for GPencil strokes 2014-10-23 16:13:48 +13:00
516f551052 Modified GPencil pie menu to work with edit modes 2014-10-23 14:12:05 +13:00
5035eef260 Hack: Use same handling logic for finding "active" GPencil block in timeline as for 3D View
This is not a terribly nice hack, but it gives the behaviour that users expect
more often than not. Namely, this enables:
* GPencil keyframes now show up in timeline
* The next/prev keyframe buttons on the Timeline header work correctly
  (i.e. they can "see" the GPencil keyframes)
2014-10-23 02:41:57 +13:00
3d5317febd Keymaps UI now shows stroke editing keymap 2014-10-23 02:36:12 +13:00
b800500814 DKEY-Tab toggles GPencil Stroke Editing 2014-10-23 02:21:39 +13:00
4793302674 UI: Add toggle for stroke editmode to sequencer properties panel (since it doesn't have a toolbar) 2014-10-23 02:21:19 +13:00
bb45d48c1b GPencil Editing: "Stroke Edit Mode"
Changed the way that the keybindings for the editing tools for strokes work,
to be less clunky to work with.

Now, instead of holding down DKEY, you instead enter "Stroke Edit Mode" (via
a toggle button in the "Edit Strokes" button in the toolbar). This enables a
keymap (Grease Pencil Stroke Edit Mode) which is only active when this setting
is enabled; the keybindings in this keymap are designed so that pressing the
normal hotkeys will affect the strokes as expected (instead of whatever else
may be in the viewport at the time instead).
2014-10-23 02:11:25 +13:00
ead565678b Fix compiler warning from changing some types 2014-10-23 01:59:46 +13:00
2c1bcad26b Code cleanup: Port flags for bGPdata to a enum 2014-10-23 01:20:16 +13:00
ef71a77e56 Support for jumping between Grease Pencil keyframes
* Added support for jumping between Grease Pencil keyframes (for the currently
  visible context).

* Also added support for showing Grease Pencil keyframes in the timeline,
  though this doesn't currently work because of context issues. We need to find
  a way around not being able to know what other views are showing GP data
  (e.g. maybe default to only showing stuff for 3D views, since that's the primary
   usecase for animation-type work)?
2014-10-19 02:24:02 +13:00
3540bebf23 Added back toggle in Clip Editor to set where the datablock gets attached 2014-10-19 01:48:08 +13:00
e14b8711b3 Put in place defines for not showing GP onionskins when animating
Added some defnies which will hide GP onionskins when animation playback is running.
These don't work yet though as there's no way to access that information from
the drawing callbacks which call the GP drawing code.
2014-10-17 13:01:38 +13:00
41e5c6198f Deselecting GP strokes deselects them for all frames and all (editable) layers 2014-10-17 00:16:52 +13:00
1201c20558 Bugfix: Sequencer properties panel doesn't update when new Grease Pencil data is added 2014-10-16 23:23:08 +13:00
f0170d035b Bugfix: Sequencer Preview doesn't update when stroke selection changes 2014-10-16 23:18:50 +13:00
da5a4a2f26 Grease Pencil layers in the DopeSheet now show interactive color swatches beside the layer name now 2014-10-15 14:07:27 +13:00
388baf3b70 Updated "Frame" menu to show all the operators which work for Grease Pencil mode 2014-10-15 13:50:57 +13:00
b09ac229ca Code Cleanup: Make it easier to reuse code for drawing settings of GP Layers 2014-10-15 13:46:31 +13:00
efb32c7169 Code Cleanup: Split onion skinning drawing code out into its own function 2014-10-15 13:40:32 +13:00
bd7b692f29 Merge branch 'master' into GPencil_EditStrokes 2014-10-15 12:57:40 +13:00
c8a0e91ac2 Code cleanup: Generate error messages instead of failing silently 2014-10-15 02:32:06 +13:00
b62677210e GP DopeSheet: It is now possible to set keyframe types for Grease Pencil frames
Use the R-key shortcut as for normal keyframes
2014-10-15 02:31:41 +13:00
e9ae8a382f GP Onion Skinning: Custom before/after colours and ranges
It is now possible to set different colors for ghosts "before" and "after" the
current frame, and to separately control how many are displayed on each side.

To use custom colors, it is necessary to firstly toggle the color-wheel icon-toggle
(located beside the Onion Skinning) checkbox. This will make both before and after
strokes get drawn using custom colors. I tried having separate toggles, but that
was too clumsy.


TODO:
* Some version-patching code will be needed to initialise the colours and fields
  so that they work when loading old files. I've reused the old field for the
  "before" range, so that at least something will be usable there.
* Some code cleanup is in order to make this code a bit nicer
2014-10-15 02:03:58 +13:00
5de47a3d6e Made the threshold for selecting individual stroke verts more sensitive
That is, a wider around the cursor is now used when checking for matching verts,
which should make it easier to successfully select verts.
2014-10-15 00:43:35 +13:00
c7eee31838 Tweaks to GPencil keymaps again
* D RMB  works as Eraser again. D Ctrl RMB was a bit too clumsy to remember
  for doing this
* D Shift RMB  is now the only remaining keymap for selecting stroke verts.
  I've modified this so that it now properly toggles selections (while maintaining
  previous selections too - it's trivial to change this behaviour in the operator
  properties panel afterwards if you don't want that). This probably works well
  enough as the only direct selection operator now?
2014-10-15 00:35:09 +13:00
c6110bf156 Merge branch 'master' into GPencil_EditStrokes
Conflicts:
	release/scripts/startup/bl_ui/space_view3d_toolbar.py
2014-10-15 00:03:42 +13:00
34fe61ebcb CMake Build Fix: Forgot to add gpencil_select.c to cmake files 2014-10-14 01:17:45 +13:00
76007990d3 GPencil UI: Further cleaning up the UI to not show the "stroke placement" settings
Since we now show the "Stroke Placement" settings in the toolshelf (where available),
we can now afford to not show it in the properties too. The only exception for this
is the sequencer, where there is no toolshelf we can use.
2014-10-13 01:54:28 +13:00
c3c22afd9e Fix for error in previous commit: Layouts were not getting deactivated properly 2014-10-13 01:52:28 +13:00
6ed6dd1301 GPencil Layers UI: Rearranging buttons for a nicer flow 2014-10-13 01:43:33 +13:00
c704c69b7e Tweaks to RNA for GPencil pointers
Some tweaks to the RNA for Grease Pencil data pointers in an attempt to get
these editable via the Outliner datablocks view. It seems though that pointer
editing there is broken everywhere instead. Nevertheless, these tweaks should
make this handling more correct (i.e. refcounts are used accordingly).

For now, the best way of setting Grease Pencil data on objects, etc. is to do
so using Python
2014-10-13 01:40:10 +13:00
cd540f3937 Grease Pencil Drawing: Default to storing on the scene when painting in 3D View
Grease Pencil will now draw on scene-level when drawing in the 3D view,
unless the active object has grease pencil data assigned (i.e. for files
saved in old versions).

The per-object method proved to be far too messy and confusing in practice,
as it was often all too easy to forget which object you had painted with.

While at present, the place where it stores strokes isn't able to be configured
from the UI yet, simply hooking up a Grease Pencil datablock into an Object's
Grease Pencil data slot should work (some tweaks are coming in the following commit
to add support for this)
2014-10-13 01:22:26 +13:00
ec8ad8d0c4 Grease Pencil Layers UI: Ported properties panel to Python and UI Lists
This commit ports the Grease Pencil properties panel (which displayed the layers info)
to Python, and simplifies the design by using UI Lists instead of listing out the
contents of each layer inline. The resulting design is a lot more compact, and should
also be more easily scannable, especially when you have lots of layers.

For now, I've left all the old C-based layers code alone, but just commented out
in case we need to roll back this commit. So far, all the editors seem to be behaving
correctly, but I may have glossed over some details.
2014-10-13 00:53:45 +13:00
e3fd2fe14b Added operator to move active Grease Pencil layers up and down the list 2014-10-12 23:31:22 +13:00
8b445fba5c Added an operator to delete the active Grease Pencil layer 2014-10-12 19:38:09 +13:00
0e3ac25de0 GPencil UI: Use the icons defined in RNA 2014-10-12 19:35:30 +13:00
d5d2a289db GPencil RNA: Add icons for the toggle settings 2014-10-12 19:20:26 +13:00
c39e9e3534 Added "gpencil_data_owner" to context for getting the struct/datablock which references the "gpencil_data" instance 2014-10-12 18:50:15 +13:00
3dd6dbdc6f Change order of Grease Pencil drawing tools in the toolshelf
Draw and Erase are now on the same row, while the two line-segment drawing modes
are on the bottom row.
2014-10-12 17:36:45 +13:00
8b5a6e9323 Rename "Stroke Tools" panel to "Edit Strokes" 2014-10-12 17:33:56 +13:00
f94c969193 GPencil Toolshelf Panels - Split into two panels
* Split Grease Pencil toolshelf panels into 2: One for drawing tools, and the
  other for the new stroke-editing tools

* Added support for Grease Pencil toolshelf panels in Node Editor. As this gets
  added before all the other bits and pieces, this currently becomes the first
  tab in the menu (which is not what we want). This will do for now, since I'm not
  sure what alternatives to doing it this way exist.
2014-10-12 17:33:26 +13:00
7e61d1fc4f GPencil Toolshelf Panel: Add "drawing settings" to toolshelf
The "Drawing Settings" displayed at the bottom of the Grease Pencil data panel
(Properties Region) is now included in the toolshelf too. These really make more
sense here, as they affect how the tool will behave (while the Properties Region
is more how it gets displayed).

However, we can't fully move over to the toolbar yet, since the VSE still lacks
a toolbar for this. Every other place (View3D, MovieClip, Image) all have their
own toolbars now.
2014-10-12 17:17:10 +13:00
8abecb7a50 GPencil UI: Experimental "tool palette" pie menu
Double-tapping D brings up a pie menu providing access to many of the different
Grease Pencil drawing and editing tools.

The current implementation is a quick test of how this could work.
* Double-tapping D is the best compromise I managed to find so far which allows
  the pie to show, but without getting triggered everytime something else happens
* I've left off one or two tools since they didn't work terribly well,
  as they rely on mouse position at time of invocation
* Even now, the drawing tools take a little getting used to (i.e. you must immediately
  start drawing once you click. But, once gotten used to, it seems to be ok...
2014-10-12 02:54:01 +13:00
3437544b88 Code cleanup: Moving from custom macro-based loopers to the context-based method 2014-10-12 01:38:16 +13:00
d88288b209 GPencil Context Utility API's now work
To work around the bizarre "active object == NULL" issues when doing context
lookups, we now use the "context-free" direct-access API method for getting
the active Grease Pencil data.
2014-10-12 01:16:42 +13:00
4f6c99a53c GPencil API - API for getting active GP data without context 2014-10-12 01:06:12 +13:00
f4a1ed4351 Note on current bugs which prevent this from working... 2014-10-12 00:59:57 +13:00
38308baf1b GP Toolbar: Use context properties to detect whether to show or not 2014-10-12 00:34:49 +13:00
7d047917b5 Added convenience utilities to context for accessing current Grease Pencil data 2014-10-11 23:41:01 +13:00
ec5f0d27b5 Grease Pencil layers can be rearranged from the DopeSheet Editor 2014-10-11 22:02:49 +13:00
e2b872ccc8 Proportional editing now works for Grease Pencil stroke transforms too
There's also support for "connected" propedit mode, but without access to that
in the Object Mode (or in other contexts), it may not be available for use.
(Hence, that support is currently still untested)
2014-10-11 17:43:21 +13:00
ab62df4837 GPencil Stroke Copying: This now copies selected points into separate curves 2014-10-11 12:51:33 +13:00
779826d295 Code cleanup: Separated out selection syncing logic for strokes into an API Function 2014-10-11 11:43:43 +13:00
9921e72b72 Trying a different keymap to get these to all play nicely together
* Eraser is now:    D Ctrl RMB
* Draw poly is now: D Alt LMB

For reference, the others are:
* Freehand Drawing: D LMB
* Line Drawing:     D CTRL LMB

* Select Point Single: D RMB
* Select Point Extend: D Shift RMB
2014-10-11 11:36:27 +13:00
c10359cb81 GPencil: Added operator to allow directly clicking on strokes to select them and their points
* D RMB       = Select the stroke point under the cursor
* D Shift RMB = Select the stroke point udner the cursor, while retaining existing selection
* D W         = Select whole stroke

As a result of these changes, the eraser has had to be moved to another binding,
i.e. D X.  This doesn't really work that great since it starts working immediately.
2014-10-11 11:29:23 +13:00
ae65857471 GPencil RNA: Changing "select" setting on strokes selects/deselects all points in stroke 2014-10-10 23:22:57 +13:00
97f9b3c05f GPencil Stroke Vert drawing: 2nd Attempt
The previous version made it too difficult to identify the selected items.
This time, we make them larger than the base items instead.
2014-10-10 21:03:32 +13:00
3e0a73e45a Setting "select" property on GP Points via RNA now syncs the selection status with the stroke
This is a bit clumsy as RNA doesn't give us this info on a plate...
2014-10-10 19:45:19 +13:00
b42c5a05d5 GP Stroke Edit: Individual points can be transformed 2014-10-10 14:00:07 +13:00
9ef589cbcc Locked GP Layers cannot be edited at all - Not even for selection 2014-10-10 13:30:28 +13:00
72c19c2148 GP Stroke Editing: Individual stroke points can now be selected
The selection status of a stroke depends on whether there are any selected points.
Without any selected points, strokes will not be selected.
2014-10-10 13:28:45 +13:00
245c5d3d3a Drawing Test: Use theme setting for the base colour of unselected GP Verts when opacity < 1
The intention here is that in this case, the unselected verts will no longer stand out
enough when full opacity is used, as they would all be the same colour.
2014-10-10 13:04:47 +13:00
e744bc03e1 Added a dedicated way of drawing selected strokes and their points
This currently uses the theme setting for vertex size for the selected colour of
strokes. For the unselected colour, it still uses the colour of the stroke itself,
which only works well when the strokes are not fully opaque, and also if the
colour of the stroke is not the same as the selected colour.
2014-10-10 12:53:55 +13:00
5be7c40552 Drawing code refactoring: Move out logic for testing if a stroke can be drawn into a separate function 2014-10-10 12:24:48 +13:00
50d88a316d Bugfix: Wrong label for scale operator in toolbar 2014-10-10 11:48:38 +13:00
fb883b3262 DNA support for tagging individual stroke points as being selected 2014-09-30 01:55:12 +13:00
80c4e23531 Code cleanup - Replace #defines with enums 2014-09-30 01:46:45 +13:00
625aeb5900 GPencil Strokes - D+M can be used to mirror strokes now 2014-09-30 01:11:41 +13:00
9dd0881abb Grease Pencil Dopesheet - Selecting channels in the dopesheet changes active layer 2014-09-29 13:03:55 +13:00
3cfeeeb2ad Fix: Node editor redraws properly after stroke selection changes 2014-09-29 04:36:09 +13:00
5222194967 Toolbar buttons for Grease Pencil editing operators
TODO: Next up, a Pie Menu (bound to DKEY) would be a nice complement for this
2014-09-29 04:26:00 +13:00
0b62328bb5 GPencil Stroke Duplicate operator is now a macro which combines the copy and move into a single step 2014-09-29 04:25:59 +13:00
159caf73da Fix: Resolved crash when toggling selection of strokes 2014-09-29 04:25:58 +13:00
e456c54fa4 Fix: Skip recalc_data() processing for strokes
This isn't needed right now, but it also shouldn't just fall through to object handling
2014-09-29 04:25:57 +13:00
72e18c873e Add more sanity check error prints 2014-09-29 04:25:56 +13:00
b616bea15c Bugfix: Fix for crash after transforming strokes 2014-09-29 04:25:55 +13:00
e1a65d804d Transform tools for Grease Pencil strokes
Selected strokes can now be transformed (translated, rotate, scaled), using:
* D+G = Grab/Move
* D+R = Rotate
* D+S = Scale

Things are still rough around the edges though, and may not work well in all
cases yet. Known todos:
* There's an occasional crash after duplicating and moving a stroke
* 2D strokes (and other editors) may not work well yet
* Need to check on the case when there is no active object
2014-09-29 04:25:54 +13:00
f08fdc0b33 GPencil - Added operator to copy selected strokes 2014-09-29 04:25:53 +13:00
75b708f702 Code cleanups 2014-09-29 04:25:52 +13:00
914efb853d Replaced inline GP stroke-looping logic with macro-based iterators
These are quite rough, and we may yet just dump this info in Context instead
using the standard techniques...
2014-09-29 04:25:51 +13:00
e4d1fe7c65 GP Circle Select
It's now possible to use Circle/Brush Select (D+C) to select individual Grease Pencil
strokes. This will eventually be useful for editing operations using these.

Implementation Notes:
* This is currently a quick and dirty implementation, which just copies and pastes
  some of the logic from the eraser, and reuses some of the same utility functions
  too.
* The shared methods are now exposed internally for use in the gpencil module.
  Perhaps they should eventually get their own dedicated section/file?
2014-09-29 04:25:51 +13:00
06e188adb7 GPencil Strokes: Select All Operator
Added a "Select All" operator for Grease Pencil strokes which will
select all strokes on visible layers.

Usage:
* <D> A      = Select All  (i.e. hold down D and press A)
* <D> Ctrl I = Invert All  (NOTE: this one may be removed, as it's quite awkward)
2014-09-29 04:25:50 +13:00
0c346fab2c Added a "select" property to GPencil strokes
This property doesn't serve much purpose yet, but will soon be used to tag strokes
for modification by "in-viewport" stroke editing tools.

As a temporary visualisation hack, this uses the abandoned "debug" drawing mode
code, which plots out all the points of the stroke. We might want to replace this
with something nicer later, but this will do for now.
2014-09-29 04:25:49 +13:00
57 changed files with 3316 additions and 374 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,293 @@ 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()
col.label(text="Edit:")
subcol = col.column(align=True)
subcol.active = edit_ok
subcol.operator("gpencil.strokes_duplicate", 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'
###############################
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 Settings
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...
pie.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
# Editing tools
if gpd:
if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
# S - Select
col = pie.column()
col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
#col.operator("gpencil.select", text="Stroke Under Mouse").entire_strokes = True
# N - Move
pie.operator("transform.translate", icon='MAN_TRANS').gpencil_strokes = True
# NW - Rotate
pie.operator("transform.rotate", icon='MAN_ROT').gpencil_strokes = True
# NE - Scale
pie.operator("transform.resize", text="Scale", icon='MAN_SCALE').gpencil_strokes = True
# SW - Copy
pie.operator("gpencil.strokes_duplicate", icon='PARTICLE_PATH')
# SE - Exit Edit Mode
pie.prop(gpd, "use_stroke_edit_mode", text="Exit Edit Mode", icon='EDIT')
else:
# Toggle Edit Mode
pie.prop(gpd, "use_stroke_edit_mode", text="Enable Stroke Editing", icon='EDIT')
###############################
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 is None:
# even with no data, this operator will still work, as it makes gpd too
layout.operator("gpencil.layer_add", text="New Layer", icon='ZOOMIN')
else:
self.draw_layers(context, layout, gpd)
# only sequencer doesn't have a toolbar to show these settings in,
# so only show this for the sequencer...
if context.space_data.type == 'SEQUENCE_EDITOR':
layout.separator()
layout.separator()
layout.prop(gpd, "use_stroke_edit_mode", text="Enable Stroke Editing", icon='EDIT', toggle=True)
layout.separator()
layout.separator()
gpencil_stroke_placement_settings(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")

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

@@ -365,14 +365,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_
@@ -1148,10 +1152,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,11 @@
# <pep8 compliant>
import bpy
from bpy.types import Header, Menu, Panel
from bl_ui.properties_grease_pencil_common import (
GreasePencilDrawingToolsPanel,
GreasePencilStrokeEditPanel,
GreasePencilDataPanel,
)
class NODE_HT_header(Header):
@@ -439,6 +444,32 @@ 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
# Tool Shelf ------------------
# Grease Pencil drawing tools
class NODE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
bl_space_type = 'NODE_EDITOR'
# Grease Pencil stroke editing tools
class NODE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel):
bl_space_type = 'NODE_EDITOR'
# -----------------------------
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
from bpy.app.translations import pgettext_iface as iface_
@@ -1012,5 +1013,13 @@ 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
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
@@ -2703,6 +2704,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,
@@ -1804,11 +1807,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

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

@@ -300,6 +300,31 @@ bGPdata *gpencil_data_duplicate(bGPdata *src)
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 */
@@ -468,16 +493,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

@@ -3407,6 +3407,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 +3432,10 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float
/* just skip - drawn as widget now */
offset += ICON_WIDTH;
}
else if ((ac->datatype == ANIMCONT_GPENCIL) && (ale->type == ANIMTYPE_GPLAYER)) {
/* just skip - drawn as a widget */
offset += ICON_WIDTH;
}
}
/* step 5) draw name ............................................... */
@@ -3878,6 +3883,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)) {
@@ -3890,6 +3896,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 ((ac->datatype == ANIMCONT_GPENCIL) && (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);
uiBlockSetEmboss(block, UI_EMBOSS);
uiDefButR(block, COLOR, 1, "", offset, yminc, ICON_WIDTH, ICON_WIDTH,
&ptr, "color", -1,
0, 0, 0, 0, gpl->info);
uiBlockSetEmboss(block, UI_EMBOSSN);
offset += ICON_WIDTH;
}
}
/* step 4) draw text - check if renaming widget is in use... */

View File

@@ -136,6 +136,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;
}
}
}
@@ -184,8 +191,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:
@@ -843,6 +856,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 +1187,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 +1243,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 */
@@ -2897,7 +2958,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

@@ -1413,26 +1413,36 @@ 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);
/* 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;
/* now add the list of collected channels */
BLI_movelisttolist(anim_data, &tmp_data);
BLI_assert(BLI_listbase_is_empty(&tmp_data));
items += tmp_items;
/* 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;
}
}
}

View File

@@ -155,6 +155,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;
@@ -171,6 +172,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;
}
/* ......... */
@@ -732,6 +737,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;
@@ -924,6 +944,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,8 @@
#include "ED_gpencil.h"
#include "ED_view3d.h"
#include "UI_resources.h"
#include "gpencil_intern.h"
/* ************************************************** */
@@ -73,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;
@@ -144,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) */
@@ -160,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
@@ -200,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;
@@ -233,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++)
@@ -244,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;
@@ -261,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();
}
@@ -299,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];
@@ -448,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 */
@@ -515,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) {
@@ -534,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)
{
@@ -563,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)
@@ -578,72 +977,40 @@ 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;
/* apply volumetric setting */
if (gpl->flag & GP_LAYER_VOLUMETRIC) dflag |= GP_DRAWDATA_VOLUMETRIC;
else dflag &= ~GP_DRAWDATA_VOLUMETRIC;
/* apply fill setting */
// XXX: this is not a very good limit
if (gpl->fill[3] > 0.001f) dflag |= GP_DRAWDATA_FILL;
/* 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
* - locked layers can't be edited, so there's no point showing these verts
* as they will have no bearings on what gets edited
*/
/* XXX: perhaps we don't want to show these when users are drawing... */
if ((gpl->flag & GP_LAYER_LOCKED) == 0) {
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)
@@ -652,8 +1019,17 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy,
(gpf->flag & GP_FRAME_PAINT))
{
/* 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

@@ -253,6 +253,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

@@ -145,13 +145,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, TOG, GP_LAYER_ACTIVE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y,
but = uiDefIconButBitS(block, TOG, 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"));
uiButSetFunc(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)) {
@@ -159,7 +158,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)
@@ -184,7 +183,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", "");
}
/* ******************* Copy Selected Strokes *********************** */
static int gp_strokes_copy_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 */
/* TODO: we probably require some selected strokes too? */
return (gpl && gpl->actframe);
}
/* Make copies of selected point segments in a selected stroke */
static void gp_strokes_copy_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_strokes_copy_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *gpl;
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
*/
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0 &&
(gpl->actframe != NULL))
{
ListBase new_strokes = {NULL, NULL};
bGPDframe *gpf = gpl->actframe;
bGPDstroke *gps;
/* 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_strokes_copy_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);
}
}
/* updates */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
void GPENCIL_OT_strokes_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Strokes";
ot->idname = "GPENCIL_OT_strokes_copy";
ot->description = "Duplicate the selected Grease Pencil strokes";
/* callbacks */
ot->exec = gp_strokes_copy_exec;
ot->poll = gp_strokes_copy_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ******************* Delete Active Frame ************************ */
static int gp_actframe_delete_poll(bContext *C)

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,23 @@ 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_strokes_copy(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

@@ -36,40 +36,130 @@
#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_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);
/* 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);
/* 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_DBL_CLICK, 0, 0);
}
/* ==================== */
/* 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);
/* Editing ----------------------------------------- */
/* duplicate and move selected points */
WM_keymap_add_item(keymap, "GPENCIL_OT_strokes_duplicate", DKEY, KM_PRESS, KM_SHIFT, 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);
}
/* ==================== */
void ED_keymap_gpencil(wmKeyConfig *keyconf)
{
ed_keymap_gpencil_general(keyconf);
ed_keymap_gpencil_editing(keyconf);
}
/* ****************************************** */
@@ -80,12 +170,23 @@ 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_strokes_copy);
/* 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);
@@ -94,4 +195,17 @@ void ED_operatortypes_gpencil(void)
/* Editing (Time) --------------- */
}
void ED_operatormacros_gpencil(void)
{
wmOperatorType *ot;
wmOperatorTypeMacro *otmacro;
ot = WM_operatortype_append_macro("GPENCIL_OT_strokes_duplicate", "Duplicate Strokes",
"Make copies of the selected Grease Pencil strokes and move them",
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "GPENCIL_OT_strokes_copy");
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;
@@ -2053,6 +2017,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,658 @@
/*
* ***** 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);
}
/* ********************************************** */
/* 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);
ARegion *ar = CTX_wm_region(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);
ARegion *ar = CTX_wm_region(C);
View2D *v2d = &ar->v2d;
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);
ARegion *ar = CTX_wm_region(C);
View2D *v2d = &ar->v2d;
Scene *scene = CTX_data_scene(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);
RNA_def_boolean(ot->srna, "entire_strokes", false, "Entire Strokes", "Select entire strokes instead of just the nearest stroke vertex");
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

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

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

@@ -1352,8 +1352,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"
@@ -2150,6 +2151,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;
@@ -2177,7 +2179,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

@@ -155,6 +155,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

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

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

@@ -515,6 +515,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_refresh(sa);
}
break;
}
}

View File

@@ -52,6 +52,7 @@
/* **************************** buttons ********************************* */
#if 0
static int sequencer_grease_pencil_panel_poll(const bContext *C, PanelType *UNUSED(pt))
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
@@ -59,9 +60,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");
@@ -72,6 +75,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

@@ -585,7 +585,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 +641,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;
}
}

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

@@ -2032,6 +2032,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

@@ -107,6 +107,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"
@@ -5878,6 +5879,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
}
else if (t->options & CTX_GPENCIL_STROKES) {
/* pass */
}
else if (t->options & CTX_PAINT_CURVE) {
/* pass */
}
@@ -7227,6 +7231,179 @@ 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 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;
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;
@@ -7247,6 +7424,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

@@ -975,6 +975,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);
}

View File

@@ -543,7 +543,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");
@@ -578,7 +582,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)
@@ -598,7 +602,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)
@@ -652,7 +656,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)
@@ -675,7 +679,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)
@@ -821,7 +825,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

@@ -42,8 +42,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 +68,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 +90,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,34 +110,47 @@ 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 {
@@ -144,23 +170,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

@@ -44,6 +44,8 @@
#ifdef RNA_RUNTIME
#include "BLI_math.h"
#include "WM_api.h"
#include "BKE_gpencil.h"
@@ -64,9 +66,30 @@ 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 *UNUSED(softmin), int *UNUSED(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;
}
else {
*min = 1;
*max = 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 +129,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_countlist(&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 +167,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 +287,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 +419,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 +472,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)
@@ -477,7 +618,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 +634,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,27 +666,57 @@ 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);
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);
@@ -592,6 +785,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)
@@ -631,7 +832,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, NULL);
/* 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

@@ -2726,16 +2726,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

@@ -2425,8 +2425,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);
@@ -2594,8 +2594,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

@@ -4645,6 +4645,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");
}
@@ -4741,6 +4742,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 */