Blender  V3.3
drawgpencil.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. */
3 
8 #include <float.h>
9 #include <math.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include "MEM_guardedalloc.h"
16 
17 #include "BLI_sys_types.h"
18 
19 #include "BLI_math.h"
20 #include "BLI_polyfill_2d.h"
21 #include "BLI_utildefines.h"
22 
23 #include "BLF_api.h"
24 #include "BLT_translation.h"
25 
26 #include "DNA_brush_types.h"
27 #include "DNA_gpencil_types.h"
28 #include "DNA_material_types.h"
29 #include "DNA_object_types.h"
30 #include "DNA_scene_types.h"
31 #include "DNA_screen_types.h"
32 #include "DNA_space_types.h"
33 #include "DNA_userdef_types.h"
34 #include "DNA_view3d_types.h"
35 
36 #include "BKE_brush.h"
37 #include "BKE_context.h"
38 #include "BKE_global.h"
39 #include "BKE_gpencil.h"
40 #include "BKE_image.h"
41 #include "BKE_material.h"
42 #include "BKE_paint.h"
43 
44 #include "DEG_depsgraph.h"
45 
46 #include "WM_api.h"
47 
48 #include "GPU_batch.h"
49 #include "GPU_immediate.h"
50 #include "GPU_matrix.h"
51 #include "GPU_shader_shared.h"
52 #include "GPU_state.h"
53 #include "GPU_uniform_buffer.h"
54 
55 #include "ED_gpencil.h"
56 #include "ED_screen.h"
57 #include "ED_space_api.h"
58 #include "ED_view3d.h"
59 
60 #include "UI_interface_icons.h"
61 #include "UI_resources.h"
62 
63 #include "IMB_imbuf_types.h"
64 
65 #include "gpencil_intern.h"
66 
67 /* ************************************************** */
68 /* GREASE PENCIL DRAWING */
69 
70 /* ----- General Defines ------ */
71 /* flags for sflag */
72 typedef enum eDrawStrokeFlags {
76  GP_DRAWDATA_ONLY3D = (1 << 1),
78  GP_DRAWDATA_ONLYV2D = (1 << 2),
80  GP_DRAWDATA_ONLYI2D = (1 << 3),
84  GP_DRAWDATA_NO_XRAY = (1 << 5),
90  GP_DRAWDATA_FILL = (1 << 8),
92 
93 /* thickness above which we should use special drawing */
94 #if 0
95 # define GP_DRAWTHICKNESS_SPECIAL 3
96 #endif
97 
98 /* conversion utility (float --> normalized unsigned byte) */
99 #define F2UB(x) (uchar)(255.0f * x)
100 
101 /* ----- Tool Buffer Drawing ------ */
102 /* helper functions to set color of buffer point */
103 
105  const float ink[4],
106  uint attr_id,
107  bool fix_strength)
108 {
109  float alpha = ink[3] * pt->strength;
110  if ((fix_strength) && (alpha >= 0.1f)) {
111  alpha = 1.0f;
112  }
113  CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
114  immAttr4ub(attr_id, F2UB(ink[0]), F2UB(ink[1]), F2UB(ink[2]), F2UB(alpha));
115 }
116 
117 /* ----------- Volumetric Strokes --------------- */
118 
119 /* draw a 3D stroke in "volumetric" style */
121  int totpoints,
122  short thickness,
123  const float ink[4])
124 {
130 
133  immBegin(GPU_PRIM_POINTS, totpoints);
134 
135  const bGPDspoint *pt = points;
136  for (int i = 0; i < totpoints && pt; i++, pt++) {
137  gpencil_set_point_varying_color(pt, ink, color, false);
138  /* TODO: scale based on view transform */
139  immAttr1f(size, pt->pressure * thickness);
140  /* we can adjust size in vertex shader based on view/projection! */
141  immVertex3fv(pos, &pt->x);
142  }
143 
144  immEnd();
146  GPU_program_point_size(false);
147 }
148 
149 /* ----- Existing Strokes Drawing (3D and Point) ------ */
150 
151 /* draw a given stroke in 3d (i.e. in 3d-space) */
153  short thickness,
154  const float ink[4],
155  bool cyclic)
156 {
157  bGPDspoint *points = tgpw->gps->points;
158  int totpoints = tgpw->gps->totpoints;
159 
160  const float viewport[2] = {(float)tgpw->winx, (float)tgpw->winy};
161  const float min_thickness = 0.05f;
162 
163  float fpt[3];
164 
165  /* if cyclic needs more vertex */
166  int cyclic_add = (cyclic) ? 1 : 0;
167 
169  const struct {
170  uint pos, color, thickness;
171  } attr_id = {
173  .color = GPU_vertformat_attr_add(
175  .thickness = GPU_vertformat_attr_add(format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT),
176  };
177 
179 
180  float obj_scale = tgpw->ob ?
181  (tgpw->ob->scale[0] + tgpw->ob->scale[1] + tgpw->ob->scale[2]) / 3.0f :
182  1.0f;
183 
184  struct GPencilStrokeData gpencil_stroke_data;
185  copy_v2_v2(gpencil_stroke_data.viewport, viewport);
186  gpencil_stroke_data.pixsize = tgpw->rv3d->pixsize;
187  gpencil_stroke_data.objscale = obj_scale;
188  int keep_size = (int)((tgpw->gpd) && (tgpw->gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS));
189  gpencil_stroke_data.keep_size = keep_size;
190  gpencil_stroke_data.pixfactor = tgpw->gpd->pixfactor;
191  /* xray mode always to 3D space to avoid wrong zdepth calculation (T60051) */
192  gpencil_stroke_data.xraymode = GP_XRAY_3DSPACE;
193  gpencil_stroke_data.caps_start = tgpw->gps->caps[0];
194  gpencil_stroke_data.caps_end = tgpw->gps->caps[1];
195  gpencil_stroke_data.fill_stroke = tgpw->is_fill_stroke;
196 
198  sizeof(struct GPencilStrokeData), &gpencil_stroke_data, __func__);
199  immBindUniformBuf("gpencil_stroke_data", ubo);
200 
201  /* draw stroke curve */
202  immBeginAtMost(GPU_PRIM_LINE_STRIP_ADJ, totpoints + cyclic_add + 2);
203  const bGPDspoint *pt = points;
204 
205  for (int i = 0; i < totpoints; i++, pt++) {
206  /* first point for adjacency (not drawn) */
207  if (i == 0) {
208  gpencil_set_point_varying_color(points, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
209 
210  if ((cyclic) && (totpoints > 2)) {
211  immAttr1f(attr_id.thickness,
212  max_ff((points + totpoints - 1)->pressure * thickness, min_thickness));
213  mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x);
214  }
215  else {
216  immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, min_thickness));
217  mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x);
218  }
219  immVertex3fv(attr_id.pos, fpt);
220  }
221  /* set point */
222  gpencil_set_point_varying_color(pt, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
223  immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, min_thickness));
224  mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x);
225  immVertex3fv(attr_id.pos, fpt);
226  }
227 
228  if (cyclic && totpoints > 2) {
229  /* draw line to first point to complete the cycle */
230  immAttr1f(attr_id.thickness, max_ff(points->pressure * thickness, 1.0f));
231  mul_v3_m4v3(fpt, tgpw->diff_mat, &points->x);
232  immVertex3fv(attr_id.pos, fpt);
233 
234  /* now add adjacency point (not drawn) */
235  immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f));
236  mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x);
237  immVertex3fv(attr_id.pos, fpt);
238  }
239  /* last adjacency point (not drawn) */
240  else {
242  points + totpoints - 2, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
243 
244  immAttr1f(attr_id.thickness, max_ff((points + totpoints - 2)->pressure * thickness, 1.0f));
245  mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 2)->x);
246  immVertex3fv(attr_id.pos, fpt);
247  }
248 
249  immEnd();
251 
252  GPU_uniformbuf_free(ubo);
253 }
254 
255 /* ----- Strokes Drawing ------ */
256 
257 /* Helper for doing all the checks on whether a stroke can be drawn */
258 static bool gpencil_can_draw_stroke(const bGPDstroke *gps, const int dflag)
259 {
260  /* skip stroke if it isn't in the right display space for this drawing context */
261  /* 1) 3D Strokes */
262  if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) {
263  return false;
264  }
265  if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) {
266  return false;
267  }
268 
269  /* 2) Screen Space 2D Strokes */
270  if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) {
271  return false;
272  }
273  if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) {
274  return false;
275  }
276 
277  /* 3) Image Space (2D) */
278  if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) {
279  return false;
280  }
281  if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) {
282  return false;
283  }
284 
285  /* skip stroke if it doesn't have any valid data */
286  if ((gps->points == NULL) || (gps->totpoints < 1)) {
287  return false;
288  }
289 
290  /* stroke can be drawn */
291  return true;
292 }
293 
294 /* draw a set of strokes */
295 static void gpencil_draw_strokes(tGPDdraw *tgpw)
296 {
297  float tcolor[4];
298  short sthickness;
299  float ink[4];
300  const bool is_unique = (tgpw->gps != NULL);
301  const bool use_mat = (tgpw->gpd->mat != NULL);
302 
304 
305  /* Do not write to depth (avoid self-occlusion). */
306  bool prev_depth_mask = GPU_depth_mask_get();
307  GPU_depth_mask(false);
308 
309  bGPDstroke *gps_init = (tgpw->gps) ? tgpw->gps : tgpw->t_gpf->strokes.first;
310 
311  for (bGPDstroke *gps = gps_init; gps; gps = gps->next) {
312  /* check if stroke can be drawn */
313  if (gpencil_can_draw_stroke(gps, tgpw->dflag) == false) {
314  continue;
315  }
316  /* check if the color is visible */
317  Material *ma = (use_mat) ? tgpw->gpd->mat[gps->mat_nr] : BKE_material_default_gpencil();
318  MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL;
319 
320  if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE) ||
321  /* If onion and ghost flag do not draw. */
322  (tgpw->onion && (gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN))) {
323  continue;
324  }
325 
326  /* if disable fill, the colors with fill must be omitted too except fill boundary strokes */
327  if ((tgpw->disable_fill == 1) && (gp_style->fill_rgba[3] > 0.0f) &&
328  ((gps->flag & GP_STROKE_NOFILL) == 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW)) {
329  continue;
330  }
331 
332  /* calculate thickness */
333  sthickness = gps->thickness + tgpw->lthick;
334 
335  if (tgpw->is_fill_stroke) {
336  sthickness = (short)max_ii(1, sthickness / 2);
337  }
338 
339  if (sthickness <= 0) {
340  continue;
341  }
342 
343  /* check which stroke-drawer to use */
344  if (tgpw->dflag & GP_DRAWDATA_ONLY3D) {
345  const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY);
346 
347  if (no_xray) {
349 
350  /* first arg is normally rv3d->dist, but this isn't
351  * available here and seems to work quite well without */
352  GPU_polygon_offset(1.0f, 1.0f);
353  }
354 
355  /* 3D Stroke */
356  /* set color using material tint color and opacity */
357  if (!tgpw->onion) {
358  interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]);
359  tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity;
360  copy_v4_v4(ink, tcolor);
361  }
362  else {
363  if (tgpw->custonion) {
364  copy_v4_v4(ink, tgpw->tintcolor);
365  }
366  else {
367  ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity);
368  copy_v4_v4(ink, tcolor);
369  }
370  }
371 
372  /* if used for fill, set opacity to 1 */
373  if (tgpw->is_fill_stroke) {
374  if (ink[3] >= GPENCIL_ALPHA_OPACITY_THRESH) {
375  ink[3] = 1.0f;
376  }
377  }
378 
379  if (gp_style->mode == GP_MATERIAL_MODE_DOT) {
380  /* volumetric stroke drawing */
381  if (tgpw->disable_fill != 1) {
382  gpencil_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink);
383  }
384  }
385  else {
386  /* 3D Lines - OpenGL primitives-based */
387  if (gps->totpoints > 1) {
388  tgpw->gps = gps;
389  gpencil_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC);
390  }
391  }
392  if (no_xray) {
394 
395  GPU_polygon_offset(0.0f, 0.0f);
396  }
397  }
398  /* if only one stroke, exit from loop */
399  if (is_unique) {
400  break;
401  }
402  }
403 
404  GPU_depth_mask(prev_depth_mask);
405  GPU_program_point_size(false);
406 }
407 
408 /* ----- General Drawing ------ */
409 
411 {
412  gpencil_draw_strokes(tgpw);
413 }
typedef float(TangentPoint)[2]
#define GPENCIL_ALPHA_OPACITY_THRESH
Definition: BKE_gpencil.h:323
#define GPENCIL_STRENGTH_MIN
Definition: BKE_gpencil.h:324
General operations, lookup, etc. for materials.
struct Material * BKE_material_default_gpencil(void)
Definition: material.c:2061
MINLINE float max_ff(float a, float b)
MINLINE int max_ii(int a, int b)
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition: math_vector.c:29
unsigned int uint
Definition: BLI_sys_types.h:67
#define ARRAY_SET_ITEMS(...)
#define UNPACK3(a)
@ GP_STROKE_NOFILL
@ GP_STROKE_2DIMAGE
@ GP_STROKE_CYCLIC
@ GP_STROKE_2DSPACE
@ GP_STROKE_3DSPACE
@ GP_XRAY_3DSPACE
@ GP_DATA_STROKE_KEEPTHICKNESS
@ GP_MATERIAL_HIDE_ONIONSKIN
@ GP_MATERIAL_HIDE
@ GP_MATERIAL_FILL_SHOW
@ GP_MATERIAL_MODE_DOT
Object is a sort of wrapper for general info.
void immAttr4ub(uint attr_id, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
void immUnbindProgram(void)
void immAttr1f(uint attr_id, float x)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
void immBindUniformBuf(const char *name, GPUUniformBuf *ubo)
GPUVertFormat * immVertexFormat(void)
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void immEnd(void)
void GPU_polygon_offset(float viewdist, float dist)
Definition: gpu_matrix.cc:721
@ GPU_PRIM_LINE_STRIP_ADJ
Definition: GPU_primitive.h:31
@ GPU_PRIM_POINTS
Definition: GPU_primitive.h:19
@ GPU_SHADER_GPENCIL_STROKE
Definition: GPU_shader.h:352
@ GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR
Definition: GPU_shader.h:347
void GPU_program_point_size(bool enable)
Definition: gpu_state.cc:172
void GPU_depth_mask(bool depth)
Definition: gpu_state.cc:107
bool GPU_depth_mask_get(void)
Definition: gpu_state.cc:273
@ GPU_DEPTH_LESS_EQUAL
Definition: GPU_state.h:86
@ GPU_DEPTH_NONE
Definition: GPU_state.h:83
void GPU_depth_test(eGPUDepthTest test)
Definition: gpu_state.cc:65
struct GPUUniformBuf GPUUniformBuf
GPUUniformBuf * GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name)
void GPU_uniformbuf_free(GPUUniformBuf *ubo)
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ GPU_COMP_U8
Contains defines and structs used throughout the imbuf module.
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
eDrawStrokeFlags
Definition: annotate_draw.c:57
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
eDrawStrokeFlags
Definition: drawgpencil.c:72
@ GP_DRAWDATA_ONLYV2D
Definition: drawgpencil.c:78
@ GP_DRAWDATA_ONLY3D
Definition: drawgpencil.c:76
@ GP_DRAWDATA_FILL
Definition: drawgpencil.c:90
@ GP_DRAWDATA_NO_ONIONS
Definition: drawgpencil.c:86
@ GP_DRAWDATA_IEDITHACK
Definition: drawgpencil.c:82
@ GP_DRAWDATA_NO_XRAY
Definition: drawgpencil.c:84
@ GP_DRAWDATA_ONLYI2D
Definition: drawgpencil.c:80
@ GP_DRAWDATA_VOLUMETRIC
Definition: drawgpencil.c:88
@ GP_DRAWDATA_NOSTATUS
Definition: drawgpencil.c:74
#define F2UB(x)
Definition: drawgpencil.c:99
static void gpencil_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4], bool cyclic)
Definition: drawgpencil.c:152
static bool gpencil_can_draw_stroke(const bGPDstroke *gps, const int dflag)
Definition: drawgpencil.c:258
static void gpencil_draw_stroke_volumetric_3d(const bGPDspoint *points, int totpoints, short thickness, const float ink[4])
Definition: drawgpencil.c:120
static void gpencil_set_point_varying_color(const bGPDspoint *pt, const float ink[4], uint attr_id, bool fix_strength)
Definition: drawgpencil.c:104
static void gpencil_draw_strokes(tGPDdraw *tgpw)
Definition: drawgpencil.c:295
void ED_gpencil_draw_fill(tGPDdraw *tgpw)
Definition: drawgpencil.c:410
uint pos
struct @653::@656 attr_id
format
Definition: logImageCore.h:38
void * first
Definition: DNA_listBase.h:31
struct MaterialGPencilStyle * gp_style
float scale[3]
ListBase strokes
bGPDspoint * points
struct bGPDstroke * next
struct Material ** mat
short lthick
float diff_mat[4][4]
int disable_fill
struct bGPdata * gpd
bool custonion
struct bGPDstroke * gps
float opacity
struct Object * ob
bool is_fill_stroke
float tintcolor[4]
struct bGPDframe * t_gpf
struct RegionView3D * rv3d