Blender V4.3
gpencil_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2014 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10#include <cmath>
11#include <cstddef>
12#include <cstdio>
13#include <cstdlib>
14#include <cstring>
15
16#include "MEM_guardedalloc.h"
17
18#include "BLI_blenlib.h"
19#include "BLI_ghash.h"
20#include "BLI_hash.h"
21#include "BLI_lasso_2d.hh"
22#include "BLI_math_color.h"
23#include "BLI_math_matrix.h"
24#include "BLI_math_vector.hh"
25#include "BLI_time.h"
26#include "BLI_utildefines.h"
27
28#include "BLT_translation.hh"
29
30#include "DNA_brush_types.h"
32#include "DNA_material_types.h"
33#include "DNA_meshdata_types.h"
34#include "DNA_object_types.h"
35#include "DNA_scene_types.h"
36#include "DNA_screen_types.h"
37#include "DNA_space_types.h"
38#include "DNA_view3d_types.h"
39
40#include "BKE_action.hh"
41#include "BKE_brush.hh"
42#include "BKE_collection.hh"
43#include "BKE_colortools.hh"
44#include "BKE_context.hh"
45#include "BKE_deform.hh"
48#include "BKE_gpencil_legacy.h"
49#include "BKE_main.hh"
50#include "BKE_material.h"
51#include "BKE_object.hh"
52#include "BKE_paint.hh"
53#include "BKE_preview_image.hh"
54#include "BKE_tracking.h"
55
56#include "WM_api.hh"
57#include "WM_toolsystem.hh"
58#include "WM_types.hh"
59
60#include "RNA_access.hh"
61#include "RNA_define.hh"
62#include "RNA_enum_types.hh"
63#include "RNA_prototypes.hh"
64
65#include "UI_resources.hh"
66#include "UI_view2d.hh"
67
68#include "ED_clip.hh"
69#include "ED_gpencil_legacy.hh"
70#include "ED_object.hh"
71#include "ED_select_utils.hh"
73#include "ED_view3d.hh"
74
75#include "GPU_immediate.hh"
76#include "GPU_immediate_util.hh"
77#include "GPU_state.hh"
78
79#include "DEG_depsgraph.hh"
81
82#include "gpencil_intern.hh"
83
84/* ******************************************************** */
85/* Context Wrangling... */
86
88{
89 /* if there's an active area, check if the particular editor may
90 * have defined any special Grease Pencil context for editing...
91 */
92 if (area) {
93 switch (area->spacetype) {
94 case SPACE_PROPERTIES: /* properties */
95 case SPACE_INFO: /* header info */
96 case SPACE_TOPBAR: /* Top-bar */
97 case SPACE_VIEW3D: /* 3D-View */
98 {
99 if (ob && (ob->type == OB_GPENCIL_LEGACY)) {
100 /* GP Object. */
101 if (r_ptr) {
102 *r_ptr = RNA_id_pointer_create(&ob->id);
103 }
104 return (bGPdata **)&ob->data;
105 }
106 return nullptr;
107 }
108 default: /* Unsupported space. */
109 return nullptr;
110 }
111 }
112
113 return nullptr;
114}
115
117 ScrArea *area,
118 Scene *scene,
119 PointerRNA *r_ptr)
120{
121 /* If there's an active area, check if the particular editor may
122 * have defined any special Grease Pencil context for editing. */
123 if (area) {
124 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
125
126 switch (area->spacetype) {
127 case SPACE_INFO: /* header info */
128 {
129 return nullptr;
130 }
131
132 case SPACE_TOPBAR: /* Top-bar */
133 case SPACE_VIEW3D: /* 3D-View */
134 case SPACE_PROPERTIES: /* properties */
135 {
136 if (r_ptr) {
137 *r_ptr = RNA_id_pointer_create(&scene->id);
138 }
139 return &scene->gpd;
140 }
141 case SPACE_NODE: /* Nodes Editor */
142 {
143 SpaceNode *snode = (SpaceNode *)sl;
144
145 /* return the GP data for the active node block/node */
146 if (snode && snode->nodetree) {
147 /* for now, as long as there's an active node tree,
148 * default to using that in the Nodes Editor */
149 if (r_ptr) {
150 *r_ptr = RNA_id_pointer_create(&snode->nodetree->id);
151 }
152 return &snode->nodetree->gpd;
153 }
154
155 /* Even when there is no node-tree, don't allow this to flow to scene. */
156 return nullptr;
157 }
158 case SPACE_SEQ: /* Sequencer */
159 {
160 SpaceSeq *sseq = (SpaceSeq *)sl;
161
162 /* For now, Grease Pencil data is associated with the space
163 * (actually preview region only). */
164 if (r_ptr) {
165 *r_ptr = RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq);
166 }
167 return &sseq->gpd;
168 }
169 case SPACE_IMAGE: /* Image/UV Editor */
170 {
171 SpaceImage *sima = (SpaceImage *)sl;
172
173 /* For now, Grease Pencil data is associated with the space... */
174 if (r_ptr) {
175 *r_ptr = RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima);
176 }
177 return &sima->gpd;
178 }
179 case SPACE_CLIP: /* Nodes Editor */
180 {
181 SpaceClip *sc = (SpaceClip *)sl;
183
184 if (clip) {
187 &clip->tracking);
188 MovieTrackingTrack *track = tracking_object->active_track;
189
190 if (!track) {
191 return nullptr;
192 }
193
194 if (r_ptr) {
195 *r_ptr = RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track);
196 }
197 return &track->gpd;
198 }
199 if (r_ptr) {
200 *r_ptr = RNA_id_pointer_create(&clip->id);
201 }
202 return &clip->gpd;
203 }
204 break;
205 }
206 default: /* unsupported space */
207 return nullptr;
208 }
209 }
210
211 return nullptr;
212}
213
215{
216 ScrArea *area = CTX_wm_area(C);
218
219 return ED_gpencil_data_get_pointers_direct(area, ob, r_ptr);
220}
221
223{
224 ID *screen_id = (ID *)CTX_wm_screen(C);
225 Scene *scene = CTX_data_scene(C);
226 ScrArea *area = CTX_wm_area(C);
227
228 return ED_annotation_data_get_pointers_direct(screen_id, area, scene, r_ptr);
229}
230/* -------------------------------------------------------- */
231
233{
234 bGPdata **gpd_ptr = ED_annotation_data_get_pointers_direct(screen_id, area, scene, nullptr);
235 return (gpd_ptr) ? *(gpd_ptr) : nullptr;
236}
237
239{
241 if ((ob == nullptr) || (ob->type != OB_GPENCIL_LEGACY)) {
242 return nullptr;
243 }
244 return static_cast<bGPdata *>(ob->data);
245}
246
248{
249 bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, nullptr);
250 return (gpd_ptr) ? *(gpd_ptr) : nullptr;
251}
252
253/* -------------------------------------------------------- */
254
256{
257 /* Key Assumption: If the pointer is an object, we're dealing with a GP Object's data.
258 * Otherwise, the GP data-block is being used for annotations (i.e. everywhere else). */
259 return ((owner_ptr) && (owner_ptr->type != &RNA_Object));
260}
261
262/* ******************************************************** */
263/* Brush Tool Core */
264
265bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
266{
267 /* simple within-radius check for now */
268 const float screen_co_a[2] = {float(x0), float(y0)};
269 const float screen_co_b[2] = {float(x1), float(y1)};
270
271 if (edge_inside_circle(mval, rad, screen_co_a, screen_co_b)) {
272 return true;
273 }
274
275 /* not inside */
276 return false;
277}
278
279/* ******************************************************** */
280/* Stroke Validity Testing */
281
283{
284 /* sanity check */
285 if (ELEM(nullptr, area, gps)) {
286 return false;
287 }
288
289 /* filter stroke types by flags + spacetype */
290 if (gps->flag & GP_STROKE_3DSPACE) {
291 /* 3D strokes - only in 3D view */
292 return ELEM(area->spacetype, SPACE_VIEW3D, SPACE_PROPERTIES);
293 }
294 if (gps->flag & GP_STROKE_2DIMAGE) {
295 /* Special "image" strokes - only in Image Editor */
296 return (area->spacetype == SPACE_IMAGE);
297 }
298 if (gps->flag & GP_STROKE_2DSPACE) {
299 /* 2D strokes (data-space) - for any 2D view (i.e. everything other than 3D view). */
300 return (area->spacetype != SPACE_VIEW3D);
301 }
302 /* view aligned - anything goes */
303 return true;
304}
305
307{
308 ScrArea *area = CTX_wm_area(C);
309 return ED_gpencil_stroke_can_use_direct(area, gps);
310}
311
313{
314 /* check if the color is editable */
316
317 if (gp_style != nullptr) {
318 if (gp_style->flag & GP_MATERIAL_HIDE) {
319 return false;
320 }
321 if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_MATERIAL_LOCKED)) {
322 return false;
323 }
324 }
325
326 return true;
327}
328
329/* ******************************************************** */
330/* Space Conversion */
331
333 const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, int *r_x, int *r_y)
334{
335 const ARegion *region = gsc->region;
336 const View2D *v2d = gsc->v2d;
337 const rctf *subrect = gsc->subrect;
338 int xyval[2];
339
340 /* sanity checks */
343
344 if (gps->flag & GP_STROKE_3DSPACE) {
346 {
347 *r_x = xyval[0];
348 *r_y = xyval[1];
349 }
350 else {
351 *r_x = V2D_IS_CLIPPED;
352 *r_y = V2D_IS_CLIPPED;
353 }
354 }
355 else if (gps->flag & GP_STROKE_2DSPACE) {
356 float vec[3] = {pt->x, pt->y, 0.0f};
357 mul_m4_v3(gsc->mat, vec);
358 UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y);
359 }
360 else {
361 if (subrect == nullptr) {
362 /* normal 3D view (or view space) */
363 *r_x = int(pt->x / 100 * region->winx);
364 *r_y = int(pt->y / 100 * region->winy);
365 }
366 else {
367 /* camera view, use subrect */
368 *r_x = int((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin;
369 *r_y = int((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin;
370 }
371 }
372}
373
375 const Object *ob,
376 char align_flag,
377 float r_vec[3])
378{
379 const float *fp = scene->cursor.location;
380
381 /* if using a gpencil object at cursor mode, can use the location of the object */
382 if (align_flag & GP_PROJECT_VIEWSPACE) {
383 if (ob && (ob->type == OB_GPENCIL_LEGACY)) {
384 /* fallback (no strokes) - use cursor or object location */
385 if (align_flag & GP_PROJECT_CURSOR) {
386 /* use 3D-cursor */
387 copy_v3_v3(r_vec, fp);
388 }
389 else {
390 /* use object location */
391 copy_v3_v3(r_vec, ob->object_to_world().location());
392 /* Apply layer offset. */
393 bGPdata *gpd = static_cast<bGPdata *>(ob->data);
395 if (gpl != nullptr) {
396 add_v3_v3(r_vec, gpl->layer_mat[3]);
397 }
398 }
399 }
400 }
401 else {
402 /* use 3D-cursor */
403 copy_v3_v3(r_vec, fp);
404 }
405}
406
411 const tGPspoint *point2D,
412 const float origin[3],
413 float out[3])
414{
415 float mval_prj[2];
416 float rvec[3];
417
418 copy_v3_v3(rvec, origin);
419
420 const float zfac = ED_view3d_calc_zfac(static_cast<const RegionView3D *>(region->regiondata),
421 rvec);
422
424 {
425 float dvec[3];
426 float xy_delta[2];
427 sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy);
428 ED_view3d_win_to_delta(region, xy_delta, zfac, dvec);
429 sub_v3_v3v3(out, rvec, dvec);
430 }
431 else {
432 zero_v3(out);
433 }
434}
435
437 float origin[3],
438 const tGPspoint *tpt,
439 bGPDspoint *pt)
440{
441 float p3d[3];
442 /* conversion to 3d format */
443 gpencil_stroke_convertcoords(region, tpt, origin, p3d);
444 copy_v3_v3(&pt->x, p3d);
445 zero_v4(pt->vert_color);
446
447 pt->pressure = tpt->pressure;
448 pt->strength = tpt->strength;
449 pt->uv_fac = tpt->uv_fac;
450 pt->uv_rot = tpt->uv_rot;
451}
452
454 int *buffer_size,
455 int *buffer_used,
456 const bool clear)
457{
458 tGPspoint *p = nullptr;
459
460 /* By default a buffer is created with one block with a predefined number of free points,
461 * if the size is not enough, the cache is reallocated adding a new block of free points.
462 * This is done in order to keep cache small and improve speed. */
463 if (*buffer_used + 1 > *buffer_size) {
464 if ((*buffer_size == 0) || (buffer_array == nullptr)) {
465 p = static_cast<tGPspoint *>(
466 MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_CHUNK, "GPencil Sbuffer"));
467 *buffer_size = GP_STROKE_BUFFER_CHUNK;
468 }
469 else {
470 *buffer_size += GP_STROKE_BUFFER_CHUNK;
471 p = static_cast<tGPspoint *>(MEM_recallocN(buffer_array, sizeof(tGPspoint) * *buffer_size));
472 }
473
474 if (p == nullptr) {
475 *buffer_size = *buffer_used = 0;
476 }
477
478 buffer_array = p;
479 }
480
481 /* clear old data */
482 if (clear) {
483 *buffer_used = 0;
484 if (buffer_array != nullptr) {
485 memset(buffer_array, 0, sizeof(tGPspoint) * *buffer_size);
486 }
487 }
488
489 return buffer_array;
490}
Blender kernel action and pose functionality.
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
support for deformation groups and hooks.
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
General operations, lookup, etc. for materials.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
General operations, lookup, etc. for blender objects.
struct MovieTrackingObject * BKE_tracking_object_get_active(const struct MovieTracking *tracking)
#define BLI_assert(a)
Definition BLI_assert.h:50
void mul_m4_v3(const float M[4][4], float r[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v4(float r[4])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
Platform independent time functions.
#define ELEM(...)
@ GP_LAYER_UNLOCK_COLOR
@ GP_MATERIAL_LOCKED
@ GP_MATERIAL_HIDE
Object is a sort of wrapper for general info.
@ OB_GPENCIL_LEGACY
@ GP_PROJECT_VIEWSPACE
@ GP_PROJECT_CURSOR
@ SPACE_CLIP
@ SPACE_TOPBAR
@ SPACE_NODE
@ SPACE_PROPERTIES
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_VIEW3D
@ SPACE_INFO
@ SC_GPENCIL_SRC_TRACK
MovieClip * ED_space_clip_get_clip(const SpaceClip *sc)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:266
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:243
void ED_view3d_win_to_delta(const ARegion *region, const float xy_delta[2], float zfac, float r_out[3])
float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3])
bool edge_inside_circle(const float cent[2], float radius, const float screen_co_a[2], const float screen_co_b[2])
eV3DProjStatus ED_view3d_project_int_global(const ARegion *region, const float co[3], int r_co[2], eV3DProjTest flag)
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
bool UI_view2d_view_to_region_clip(const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1697
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define GP_STROKE_BUFFER_CHUNK
bGPdata ** ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
bool ED_gpencil_stroke_material_editable(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps)
bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps)
bGPdata * ED_annotation_data_get_active(const bContext *C)
bGPdata * ED_gpencil_data_get_active(const bContext *C)
static void gpencil_stroke_convertcoords(ARegion *region, const tGPspoint *point2D, const float origin[3], float out[3])
bGPdata * ED_annotation_data_get_active_direct(ID *screen_id, ScrArea *area, Scene *scene)
bGPdata ** ED_annotation_data_get_pointers_direct(ID *screen_id, ScrArea *area, Scene *scene, PointerRNA *r_ptr)
void ED_gpencil_tpoint_to_point(ARegion *region, float origin[3], const tGPspoint *tpt, bGPDspoint *pt)
void gpencil_point_to_xy(const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, int *r_x, int *r_y)
bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps)
void ED_gpencil_drawing_reference_get(const Scene *scene, const Object *ob, char align_flag, float r_vec[3])
bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr)
bGPdata ** ED_gpencil_data_get_pointers_direct(ScrArea *area, Object *ob, PointerRNA *r_ptr)
bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
tGPspoint * ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array, int *buffer_size, int *buffer_used, const bool clear)
bGPdata ** ED_annotation_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static void clear(Message &msg)
Definition msgfmt.cc:218
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
Definition DNA_ID.h:413
MovieTrackingTrack * active_track
StructRNA * type
Definition RNA_types.hh:41
struct bGPdata * gpd
struct bNodeTree * nodetree
struct bGPdata * gpd
struct bGPdata * gpd
float xmin
float ymin