During drawing, the depsgrah was tagged to update and this produced a full copy of the datablock. This tag was done in order to get the right data in drawing engine, but this added a great overhead while drawing and the response of the pen was not good. Now, the depsgraph is not tagged and the drawing engine uses the original copy data of the buffer datablock. This is not a problem because only can draw in one window at time.
1336 lines
43 KiB
C
1336 lines
43 KiB
C
/*
|
|
* Copyright 2017, Blender Foundation.
|
|
*
|
|
* 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.
|
|
*
|
|
* Contributor(s): Antonio Vazquez
|
|
*
|
|
*/
|
|
|
|
/** \file blender/draw/engines/gpencil/gpencil_draw_utils.c
|
|
* \ingroup draw
|
|
*/
|
|
|
|
#include "BLI_polyfill_2d.h"
|
|
|
|
#include "DRW_engine.h"
|
|
#include "DRW_render.h"
|
|
|
|
#include "BKE_brush.h"
|
|
#include "BKE_gpencil.h"
|
|
#include "BKE_gpencil_modifier.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_material.h"
|
|
|
|
#include "ED_gpencil.h"
|
|
#include "ED_view3d.h"
|
|
|
|
#include "DNA_gpencil_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_view3d_types.h"
|
|
#include "DNA_gpencil_modifier_types.h"
|
|
|
|
/* If builtin shaders are needed */
|
|
#include "GPU_shader.h"
|
|
#include "GPU_texture.h"
|
|
|
|
/* For EvaluationContext... */
|
|
#include "DEG_depsgraph.h"
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "gpencil_engine.h"
|
|
|
|
/* fill type to communicate to shader */
|
|
#define SOLID 0
|
|
#define GRADIENT 1
|
|
#define RADIAL 2
|
|
#define CHESS 3
|
|
#define TEXTURE 4
|
|
#define PATTERN 5
|
|
|
|
/* Helper for doing all the checks on whether a stroke can be drawn */
|
|
static bool gpencil_can_draw_stroke(
|
|
struct MaterialGPencilStyle *gp_style, const bGPDstroke *gps,
|
|
const bool onion, const bool is_mat_preview)
|
|
{
|
|
/* skip stroke if it doesn't have any valid data */
|
|
if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL))
|
|
return false;
|
|
|
|
/* if mat preview render always visible */
|
|
if (is_mat_preview) {
|
|
return true;
|
|
}
|
|
|
|
/* check if the color is visible */
|
|
if ((gp_style == NULL) ||
|
|
(gp_style->flag & GP_STYLE_COLOR_HIDE) ||
|
|
(onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* stroke can be drawn */
|
|
return true;
|
|
}
|
|
|
|
/* calc bounding box in 2d using flat projection data */
|
|
static void gpencil_calc_2d_bounding_box(
|
|
const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand)
|
|
{
|
|
minv[0] = points2d[0][0];
|
|
minv[1] = points2d[0][1];
|
|
maxv[0] = points2d[0][0];
|
|
maxv[1] = points2d[0][1];
|
|
|
|
for (int i = 1; i < totpoints; i++) {
|
|
/* min */
|
|
if (points2d[i][0] < minv[0]) {
|
|
minv[0] = points2d[i][0];
|
|
}
|
|
if (points2d[i][1] < minv[1]) {
|
|
minv[1] = points2d[i][1];
|
|
}
|
|
/* max */
|
|
if (points2d[i][0] > maxv[0]) {
|
|
maxv[0] = points2d[i][0];
|
|
}
|
|
if (points2d[i][1] > maxv[1]) {
|
|
maxv[1] = points2d[i][1];
|
|
}
|
|
}
|
|
/* If not expanded, use a perfect square */
|
|
if (expand == false) {
|
|
if (maxv[0] > maxv[1]) {
|
|
maxv[1] = maxv[0];
|
|
}
|
|
else {
|
|
maxv[0] = maxv[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* calc texture coordinates using flat projected points */
|
|
static void gpencil_calc_stroke_fill_uv(
|
|
const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], float(*r_uv)[2])
|
|
{
|
|
float d[2];
|
|
d[0] = maxv[0] - minv[0];
|
|
d[1] = maxv[1] - minv[1];
|
|
for (int i = 0; i < totpoints; i++) {
|
|
r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
|
|
r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
|
|
}
|
|
}
|
|
|
|
/* Get points of stroke always flat to view not affected by camera view or view position */
|
|
static void gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction)
|
|
{
|
|
const bGPDspoint *pt0 = &points[0];
|
|
const bGPDspoint *pt1 = &points[1];
|
|
const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
|
|
|
|
float locx[3];
|
|
float locy[3];
|
|
float loc3[3];
|
|
float normal[3];
|
|
|
|
/* local X axis (p0 -> p1) */
|
|
sub_v3_v3v3(locx, &pt1->x, &pt0->x);
|
|
|
|
/* point vector at 3/4 */
|
|
sub_v3_v3v3(loc3, &pt3->x, &pt0->x);
|
|
|
|
/* vector orthogonal to polygon plane */
|
|
cross_v3_v3v3(normal, locx, loc3);
|
|
|
|
/* local Y axis (cross to normal/x axis) */
|
|
cross_v3_v3v3(locy, normal, locx);
|
|
|
|
/* Normalize vectors */
|
|
normalize_v3(locx);
|
|
normalize_v3(locy);
|
|
|
|
/* Get all points in local space */
|
|
for (int i = 0; i < totpoints; i++) {
|
|
const bGPDspoint *pt = &points[i];
|
|
float loc[3];
|
|
|
|
/* Get local space using first point as origin */
|
|
sub_v3_v3v3(loc, &pt->x, &pt0->x);
|
|
|
|
points2d[i][0] = dot_v3v3(loc, locx);
|
|
points2d[i][1] = dot_v3v3(loc, locy);
|
|
}
|
|
|
|
/* Concave (-1), Convex (1), or Autodetect (0)? */
|
|
*r_direction = (int)locy[2];
|
|
}
|
|
|
|
/* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */
|
|
void DRW_gpencil_triangulate_stroke_fill(bGPDstroke *gps)
|
|
{
|
|
BLI_assert(gps->totpoints >= 3);
|
|
|
|
/* allocate memory for temporary areas */
|
|
gps->tot_triangles = gps->totpoints - 2;
|
|
uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation");
|
|
float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points");
|
|
float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
|
|
|
|
int direction = 0;
|
|
|
|
/* convert to 2d and triangulate */
|
|
gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
|
|
BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
|
|
|
|
/* calc texture coordinates automatically */
|
|
float minv[2];
|
|
float maxv[2];
|
|
/* first needs bounding box data */
|
|
gpencil_calc_2d_bounding_box(points2d, gps->totpoints, minv, maxv, false);
|
|
/* calc uv data */
|
|
gpencil_calc_stroke_fill_uv(points2d, gps->totpoints, minv, maxv, uv);
|
|
|
|
/* Number of triangles */
|
|
gps->tot_triangles = gps->totpoints - 2;
|
|
/* save triangulation data in stroke cache */
|
|
if (gps->tot_triangles > 0) {
|
|
if (gps->triangles == NULL) {
|
|
gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, "GP Stroke triangulation");
|
|
}
|
|
else {
|
|
gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles);
|
|
}
|
|
|
|
for (int i = 0; i < gps->tot_triangles; i++) {
|
|
bGPDtriangle *stroke_triangle = &gps->triangles[i];
|
|
memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
|
|
/* copy texture coordinates */
|
|
copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]);
|
|
copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]);
|
|
copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]);
|
|
}
|
|
}
|
|
else {
|
|
/* No triangles needed - Free anything allocated previously */
|
|
if (gps->triangles)
|
|
MEM_freeN(gps->triangles);
|
|
|
|
gps->triangles = NULL;
|
|
}
|
|
|
|
/* disable recalculation flag */
|
|
if (gps->flag & GP_STROKE_RECALC_CACHES) {
|
|
gps->flag &= ~GP_STROKE_RECALC_CACHES;
|
|
}
|
|
|
|
/* clear memory */
|
|
MEM_SAFE_FREE(tmp_triangles);
|
|
MEM_SAFE_FREE(points2d);
|
|
MEM_SAFE_FREE(uv);
|
|
}
|
|
|
|
/* recalc the internal geometry caches for fill and uvs */
|
|
static void DRW_gpencil_recalc_geometry_caches(Object *ob, MaterialGPencilStyle *gp_style, bGPDstroke *gps)
|
|
{
|
|
if (gps->flag & GP_STROKE_RECALC_CACHES) {
|
|
/* Calculate triangles cache for filling area (must be done only after changes) */
|
|
if ((gps->tot_triangles == 0) || (gps->triangles == NULL)) {
|
|
if ((gps->totpoints > 2) &&
|
|
((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)))
|
|
{
|
|
DRW_gpencil_triangulate_stroke_fill(gps);
|
|
}
|
|
}
|
|
|
|
/* calc uv data along the stroke */
|
|
ED_gpencil_calc_stroke_uv(ob, gps);
|
|
|
|
/* clear flag */
|
|
gps->flag &= ~GP_STROKE_RECALC_CACHES;
|
|
}
|
|
}
|
|
|
|
/* create shading group for filling */
|
|
static DRWShadingGroup *DRW_gpencil_shgroup_fill_create(
|
|
GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass,
|
|
GPUShader *shader, bGPdata *gpd, MaterialGPencilStyle *gp_style, int id)
|
|
{
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
|
|
/* e_data.gpencil_fill_sh */
|
|
DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
|
|
|
|
DRW_shgroup_uniform_vec4(grp, "color2", gp_style->mix_rgba, 1);
|
|
|
|
/* set style type */
|
|
switch (gp_style->fill_style) {
|
|
case GP_STYLE_FILL_STYLE_SOLID:
|
|
stl->shgroups[id].fill_style = SOLID;
|
|
break;
|
|
case GP_STYLE_FILL_STYLE_GRADIENT:
|
|
if (gp_style->gradient_type == GP_STYLE_GRADIENT_LINEAR) {
|
|
stl->shgroups[id].fill_style = GRADIENT;
|
|
}
|
|
else {
|
|
stl->shgroups[id].fill_style = RADIAL;
|
|
}
|
|
break;
|
|
case GP_STYLE_FILL_STYLE_CHESSBOARD:
|
|
stl->shgroups[id].fill_style = CHESS;
|
|
break;
|
|
case GP_STYLE_FILL_STYLE_TEXTURE:
|
|
if (gp_style->flag & GP_STYLE_FILL_PATTERN) {
|
|
stl->shgroups[id].fill_style = PATTERN;
|
|
}
|
|
else {
|
|
stl->shgroups[id].fill_style = TEXTURE;
|
|
}
|
|
break;
|
|
default:
|
|
stl->shgroups[id].fill_style = GP_STYLE_FILL_STYLE_SOLID;
|
|
break;
|
|
}
|
|
DRW_shgroup_uniform_int(grp, "fill_type", &stl->shgroups[id].fill_style, 1);
|
|
|
|
DRW_shgroup_uniform_float(grp, "mix_factor", &gp_style->mix_factor, 1);
|
|
|
|
DRW_shgroup_uniform_float(grp, "gradient_angle", &gp_style->gradient_angle, 1);
|
|
DRW_shgroup_uniform_float(grp, "gradient_radius", &gp_style->gradient_radius, 1);
|
|
DRW_shgroup_uniform_float(grp, "pattern_gridsize", &gp_style->pattern_gridsize, 1);
|
|
DRW_shgroup_uniform_vec2(grp, "gradient_scale", gp_style->gradient_scale, 1);
|
|
DRW_shgroup_uniform_vec2(grp, "gradient_shift", gp_style->gradient_shift, 1);
|
|
|
|
DRW_shgroup_uniform_float(grp, "texture_angle", &gp_style->texture_angle, 1);
|
|
DRW_shgroup_uniform_vec2(grp, "texture_scale", gp_style->texture_scale, 1);
|
|
DRW_shgroup_uniform_vec2(grp, "texture_offset", gp_style->texture_offset, 1);
|
|
DRW_shgroup_uniform_float(grp, "texture_opacity", &gp_style->texture_opacity, 1);
|
|
|
|
stl->shgroups[id].texture_mix = gp_style->flag & GP_STYLE_COLOR_TEX_MIX ? 1 : 0;
|
|
DRW_shgroup_uniform_int(grp, "texture_mix", &stl->shgroups[id].texture_mix, 1);
|
|
|
|
stl->shgroups[id].texture_flip = gp_style->flag & GP_STYLE_COLOR_FLIP_FILL ? 1 : 0;
|
|
DRW_shgroup_uniform_int(grp, "texture_flip", &stl->shgroups[id].texture_flip, 1);
|
|
|
|
DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1);
|
|
/* image texture */
|
|
if ((gp_style->flag & GP_STYLE_COLOR_TEX_MIX) ||
|
|
(gp_style->fill_style & GP_STYLE_FILL_STYLE_TEXTURE))
|
|
{
|
|
ImBuf *ibuf;
|
|
Image *image = gp_style->ima;
|
|
ImageUser iuser = { NULL };
|
|
void *lock;
|
|
|
|
iuser.ok = true;
|
|
|
|
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
|
|
|
|
if (ibuf == NULL || ibuf->rect == NULL) {
|
|
BKE_image_release_ibuf(image, ibuf, NULL);
|
|
}
|
|
else {
|
|
GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, GL_TEXTURE_2D, true, 0.0);
|
|
DRW_shgroup_uniform_texture(grp, "myTexture", texture);
|
|
|
|
stl->shgroups[id].texture_clamp = gp_style->flag & GP_STYLE_COLOR_TEX_CLAMP ? 1 : 0;
|
|
DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1);
|
|
|
|
BKE_image_release_ibuf(image, ibuf, NULL);
|
|
}
|
|
}
|
|
else {
|
|
/* if no texture defined, need a blank texture to avoid errors in draw manager */
|
|
DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture);
|
|
stl->shgroups[id].texture_clamp = 0;
|
|
DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1);
|
|
}
|
|
|
|
return grp;
|
|
}
|
|
|
|
/* create shading group for strokes */
|
|
DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
|
|
GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, GPUShader *shader, Object *ob,
|
|
bGPdata *gpd, MaterialGPencilStyle *gp_style, int id, bool onion)
|
|
{
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
const float *viewport_size = DRW_viewport_size_get();
|
|
|
|
/* e_data.gpencil_stroke_sh */
|
|
DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
|
|
|
|
DRW_shgroup_uniform_vec2(grp, "Viewport", viewport_size, 1);
|
|
|
|
DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1);
|
|
DRW_shgroup_uniform_float(grp, "pixelsize", &U.pixelsize, 1);
|
|
|
|
/* avoid wrong values */
|
|
if ((gpd) && (gpd->pixfactor == 0)) {
|
|
gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
|
|
}
|
|
|
|
/* object scale and depth */
|
|
if ((ob) && (id > -1)) {
|
|
stl->shgroups[id].obj_scale = mat4_to_scale(ob->obmat);
|
|
DRW_shgroup_uniform_float(grp, "objscale", &stl->shgroups[id].obj_scale, 1);
|
|
stl->shgroups[id].keep_size = (int)((gpd) && (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS));
|
|
DRW_shgroup_uniform_int(grp, "keep_size", &stl->shgroups[id].keep_size, 1);
|
|
|
|
stl->shgroups[id].stroke_style = gp_style->stroke_style;
|
|
stl->shgroups[id].color_type = GPENCIL_COLOR_SOLID;
|
|
if ((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
|
|
stl->shgroups[id].color_type = GPENCIL_COLOR_TEXTURE;
|
|
if (gp_style->flag & GP_STYLE_STROKE_PATTERN) {
|
|
stl->shgroups[id].color_type = GPENCIL_COLOR_PATTERN;
|
|
}
|
|
}
|
|
DRW_shgroup_uniform_int(grp, "color_type", &stl->shgroups[id].color_type, 1);
|
|
DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
|
|
}
|
|
else {
|
|
stl->storage->obj_scale = 1.0f;
|
|
stl->storage->keep_size = 0;
|
|
stl->storage->pixfactor = GP_DEFAULT_PIX_FACTOR;
|
|
DRW_shgroup_uniform_float(grp, "objscale", &stl->storage->obj_scale, 1);
|
|
DRW_shgroup_uniform_int(grp, "keep_size", &stl->storage->keep_size, 1);
|
|
DRW_shgroup_uniform_int(grp, "color_type", &stl->storage->color_type, 1);
|
|
if (gpd) {
|
|
DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
|
|
}
|
|
else {
|
|
DRW_shgroup_uniform_float(grp, "pixfactor", &stl->storage->pixfactor, 1);
|
|
}
|
|
}
|
|
|
|
if ((gpd) && (id > -1)) {
|
|
DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1);
|
|
}
|
|
else {
|
|
/* for drawing always on front */
|
|
DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
|
|
}
|
|
|
|
/* image texture for pattern */
|
|
if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
|
|
ImBuf *ibuf;
|
|
Image *image = gp_style->sima;
|
|
ImageUser iuser = { NULL };
|
|
void *lock;
|
|
|
|
iuser.ok = true;
|
|
|
|
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
|
|
|
|
if (ibuf == NULL || ibuf->rect == NULL) {
|
|
BKE_image_release_ibuf(image, ibuf, NULL);
|
|
}
|
|
else {
|
|
GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D, true, 0.0f);
|
|
DRW_shgroup_uniform_texture(grp, "myTexture", texture);
|
|
|
|
BKE_image_release_ibuf(image, ibuf, NULL);
|
|
}
|
|
}
|
|
else {
|
|
/* if no texture defined, need a blank texture to avoid errors in draw manager */
|
|
DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture);
|
|
}
|
|
|
|
return grp;
|
|
}
|
|
|
|
/* create shading group for volumetrics */
|
|
static DRWShadingGroup *DRW_gpencil_shgroup_point_create(
|
|
GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, GPUShader *shader, Object *ob,
|
|
bGPdata *gpd, MaterialGPencilStyle *gp_style, int id, bool onion)
|
|
{
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
const float *viewport_size = DRW_viewport_size_get();
|
|
|
|
/* e_data.gpencil_stroke_sh */
|
|
DRWShadingGroup *grp = DRW_shgroup_create(shader, pass);
|
|
|
|
DRW_shgroup_uniform_vec2(grp, "Viewport", viewport_size, 1);
|
|
DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1);
|
|
DRW_shgroup_uniform_float(grp, "pixelsize", &U.pixelsize, 1);
|
|
|
|
/* avoid wrong values */
|
|
if ((gpd) && (gpd->pixfactor == 0)) {
|
|
gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
|
|
}
|
|
|
|
/* object scale and depth */
|
|
if ((ob) && (id > -1)) {
|
|
stl->shgroups[id].obj_scale = mat4_to_scale(ob->obmat);;
|
|
DRW_shgroup_uniform_float(grp, "objscale", &stl->shgroups[id].obj_scale, 1);
|
|
stl->shgroups[id].keep_size = (int)((gpd) && (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS));
|
|
DRW_shgroup_uniform_int(grp, "keep_size", &stl->shgroups[id].keep_size, 1);
|
|
|
|
stl->shgroups[id].mode = gp_style->mode;
|
|
stl->shgroups[id].stroke_style = gp_style->stroke_style;
|
|
stl->shgroups[id].color_type = GPENCIL_COLOR_SOLID;
|
|
if ((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
|
|
stl->shgroups[id].color_type = GPENCIL_COLOR_TEXTURE;
|
|
if (gp_style->flag & GP_STYLE_STROKE_PATTERN) {
|
|
stl->shgroups[id].color_type = GPENCIL_COLOR_PATTERN;
|
|
}
|
|
}
|
|
DRW_shgroup_uniform_int(grp, "color_type", &stl->shgroups[id].color_type, 1);
|
|
DRW_shgroup_uniform_int(grp, "mode", &stl->shgroups[id].mode, 1);
|
|
DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
|
|
}
|
|
else {
|
|
stl->storage->obj_scale = 1.0f;
|
|
stl->storage->keep_size = 0;
|
|
stl->storage->pixfactor = GP_DEFAULT_PIX_FACTOR;
|
|
stl->storage->mode = gp_style->mode;
|
|
DRW_shgroup_uniform_float(grp, "objscale", &stl->storage->obj_scale, 1);
|
|
DRW_shgroup_uniform_int(grp, "keep_size", &stl->storage->keep_size, 1);
|
|
DRW_shgroup_uniform_int(grp, "color_type", &stl->storage->color_type, 1);
|
|
DRW_shgroup_uniform_int(grp, "mode", &stl->storage->mode, 1);
|
|
if (gpd) {
|
|
DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
|
|
}
|
|
else {
|
|
DRW_shgroup_uniform_float(grp, "pixfactor", &stl->storage->pixfactor, 1);
|
|
}
|
|
}
|
|
|
|
if (gpd) {
|
|
DRW_shgroup_uniform_int(grp, "xraymode", (const int *)&gpd->xray_mode, 1);
|
|
}
|
|
else {
|
|
/* for drawing always on front */
|
|
DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1);
|
|
}
|
|
|
|
/* image texture */
|
|
if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) {
|
|
ImBuf *ibuf;
|
|
Image *image = gp_style->sima;
|
|
ImageUser iuser = { NULL };
|
|
void *lock;
|
|
|
|
iuser.ok = true;
|
|
|
|
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
|
|
|
|
if (ibuf == NULL || ibuf->rect == NULL) {
|
|
BKE_image_release_ibuf(image, ibuf, NULL);
|
|
}
|
|
else {
|
|
GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, GL_TEXTURE_2D, true, 0.0f);
|
|
DRW_shgroup_uniform_texture(grp, "myTexture", texture);
|
|
|
|
BKE_image_release_ibuf(image, ibuf, NULL);
|
|
}
|
|
}
|
|
else {
|
|
/* if no texture defined, need a blank texture to avoid errors in draw manager */
|
|
DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture);
|
|
}
|
|
|
|
return grp;
|
|
}
|
|
|
|
/* add fill shading group to pass */
|
|
static void gpencil_add_fill_shgroup(
|
|
GpencilBatchCache *cache, DRWShadingGroup *fillgrp,
|
|
Object *ob, bGPDframe *gpf, bGPDstroke *gps,
|
|
float opacity, const float tintcolor[4], const bool onion, const bool custonion)
|
|
{
|
|
MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
|
|
if (gps->totpoints >= 3) {
|
|
float tfill[4];
|
|
/* set color using material, tint color and opacity */
|
|
interp_v3_v3v3(tfill, gps->runtime.tmp_fill_rgba, tintcolor, tintcolor[3]);
|
|
tfill[3] = gps->runtime.tmp_fill_rgba[3] * opacity;
|
|
if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) {
|
|
const float *color;
|
|
if (!onion) {
|
|
color = tfill;
|
|
}
|
|
else {
|
|
if (custonion) {
|
|
color = tintcolor;
|
|
}
|
|
else {
|
|
ARRAY_SET_ITEMS(tfill, UNPACK3(gps->runtime.tmp_fill_rgba), tintcolor[3]);
|
|
color = tfill;
|
|
}
|
|
}
|
|
if (cache->is_dirty) {
|
|
gpencil_batch_cache_check_free_slots(ob);
|
|
cache->batch_fill[cache->cache_idx] = DRW_gpencil_get_fill_geom(ob, gps, color);
|
|
}
|
|
DRW_shgroup_call_add(fillgrp, cache->batch_fill[cache->cache_idx], gpf->runtime.viewmatrix);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* add stroke shading group to pass */
|
|
static void gpencil_add_stroke_shgroup(GpencilBatchCache *cache, DRWShadingGroup *strokegrp,
|
|
Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps,
|
|
const float opacity, const float tintcolor[4], const bool onion,
|
|
const bool custonion)
|
|
{
|
|
float tcolor[4];
|
|
float ink[4];
|
|
short sthickness;
|
|
MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
|
|
|
|
/* set color using base color, tint color and opacity */
|
|
if (!onion) {
|
|
/* if special stroke, use fill color as stroke color */
|
|
if (gps->flag & GP_STROKE_NOFILL) {
|
|
interp_v3_v3v3(tcolor, gps->runtime.tmp_fill_rgba, tintcolor, tintcolor[3]);
|
|
tcolor[3] = gps->runtime.tmp_fill_rgba[3] * opacity;
|
|
}
|
|
else {
|
|
interp_v3_v3v3(tcolor, gps->runtime.tmp_stroke_rgba, tintcolor, tintcolor[3]);
|
|
tcolor[3] = gps->runtime.tmp_stroke_rgba[3] * opacity;
|
|
}
|
|
copy_v4_v4(ink, tcolor);
|
|
}
|
|
else {
|
|
if (custonion) {
|
|
copy_v4_v4(ink, tintcolor);
|
|
}
|
|
else {
|
|
ARRAY_SET_ITEMS(tcolor, UNPACK3(gps->runtime.tmp_stroke_rgba), opacity);
|
|
copy_v4_v4(ink, tcolor);
|
|
}
|
|
}
|
|
|
|
sthickness = gps->thickness + gpl->line_change;
|
|
CLAMP_MIN(sthickness, 1);
|
|
if (cache->is_dirty) {
|
|
gpencil_batch_cache_check_free_slots(ob);
|
|
if ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
|
|
cache->batch_stroke[cache->cache_idx] = DRW_gpencil_get_stroke_geom(gps, sthickness, ink);
|
|
}
|
|
else {
|
|
cache->batch_stroke[cache->cache_idx] = DRW_gpencil_get_point_geom(gps, sthickness, ink);
|
|
}
|
|
}
|
|
DRW_shgroup_call_add(strokegrp, cache->batch_stroke[cache->cache_idx], gpf->runtime.viewmatrix);
|
|
}
|
|
|
|
/* add edit points shading group to pass */
|
|
static void gpencil_add_editpoints_shgroup(
|
|
GPENCIL_StorageList *stl, GpencilBatchCache *cache, ToolSettings *UNUSED(ts), Object *ob,
|
|
bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps)
|
|
{
|
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
|
View3D *v3d = draw_ctx->v3d;
|
|
MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
|
|
|
|
/* alpha factor for edit points/line to make them more subtle */
|
|
float edit_alpha = v3d->vertex_opacity;
|
|
|
|
if (GPENCIL_ANY_EDIT_MODE(gpd)) {
|
|
Object *obact = DRW_context_state_get()->obact;
|
|
if ((!obact) || (obact->type != OB_GPENCIL)) {
|
|
return;
|
|
}
|
|
const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE);
|
|
|
|
/* line of the original stroke */
|
|
if (cache->is_dirty) {
|
|
gpencil_batch_cache_check_free_slots(ob);
|
|
cache->batch_edlin[cache->cache_idx] = DRW_gpencil_get_edlin_geom(gps, edit_alpha, gpd->flag);
|
|
}
|
|
if (cache->batch_edlin[cache->cache_idx]) {
|
|
if ((obact) && (obact == ob) &&
|
|
((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
|
|
(v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES))
|
|
{
|
|
DRW_shgroup_call_add(
|
|
stl->g_data->shgrps_edit_line,
|
|
cache->batch_edlin[cache->cache_idx],
|
|
gpf->runtime.viewmatrix);
|
|
}
|
|
}
|
|
/* edit points */
|
|
if ((gps->flag & GP_STROKE_SELECT) || (is_weight_paint)) {
|
|
if ((gpl->flag & GP_LAYER_UNLOCK_COLOR) || ((gp_style->flag & GP_STYLE_COLOR_LOCKED) == 0)) {
|
|
if (cache->is_dirty) {
|
|
gpencil_batch_cache_check_free_slots(ob);
|
|
cache->batch_edit[cache->cache_idx] = DRW_gpencil_get_edit_geom(gps, edit_alpha, gpd->flag);
|
|
}
|
|
if (cache->batch_edit[cache->cache_idx]) {
|
|
if ((obact) && (obact == ob)) {
|
|
/* edit pass */
|
|
DRW_shgroup_call_add(
|
|
stl->g_data->shgrps_edit_point,
|
|
cache->batch_edit[cache->cache_idx],
|
|
gpf->runtime.viewmatrix);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* function to draw strokes for onion only */
|
|
static void gpencil_draw_onion_strokes(
|
|
GpencilBatchCache *cache, GPENCIL_e_data *e_data, void *vedata, Object *ob,
|
|
bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf,
|
|
const float opacity, const float tintcolor[4], const bool custonion)
|
|
{
|
|
GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
|
|
|
|
float viewmatrix[4][4];
|
|
|
|
/* get parent matrix and save as static data */
|
|
ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, viewmatrix);
|
|
copy_m4_m4(gpf->runtime.viewmatrix, viewmatrix);
|
|
|
|
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
|
|
MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
|
|
copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba);
|
|
copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba);
|
|
|
|
int id = stl->storage->shgroup_id;
|
|
/* check if stroke can be drawn */
|
|
if (gpencil_can_draw_stroke(gp_style, gps, true, false) == false) {
|
|
continue;
|
|
}
|
|
/* limit the number of shading groups */
|
|
if (id >= GPENCIL_MAX_SHGROUPS) {
|
|
continue;
|
|
}
|
|
|
|
stl->shgroups[id].shgrps_fill = NULL;
|
|
if ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
|
|
stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_stroke_create(
|
|
e_data, vedata, psl->stroke_pass, e_data->gpencil_stroke_sh, ob, gpd, gp_style, id, true);
|
|
}
|
|
else {
|
|
stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_point_create(
|
|
e_data, vedata, psl->stroke_pass, e_data->gpencil_point_sh, ob, gpd, gp_style, id, true);
|
|
}
|
|
|
|
/* stroke */
|
|
gpencil_add_stroke_shgroup(
|
|
cache, stl->shgroups[id].shgrps_stroke, ob, gpl, gpf, gps, opacity, tintcolor, true, custonion);
|
|
|
|
stl->storage->shgroup_id++;
|
|
cache->cache_idx++;
|
|
}
|
|
}
|
|
|
|
|
|
/* main function to draw strokes */
|
|
static void gpencil_draw_strokes(
|
|
GpencilBatchCache *cache, GPENCIL_e_data *e_data, void *vedata, ToolSettings *ts, Object *ob,
|
|
bGPdata *gpd, bGPDlayer *gpl, bGPDframe *src_gpf, bGPDframe *derived_gpf,
|
|
const float opacity, const float tintcolor[4],
|
|
const bool custonion, tGPencilObjectCache *cache_ob)
|
|
{
|
|
GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
|
Scene *scene = draw_ctx->scene;
|
|
View3D *v3d = draw_ctx->v3d;
|
|
bGPDstroke *gps, *src_gps;
|
|
DRWShadingGroup *fillgrp;
|
|
DRWShadingGroup *strokegrp;
|
|
float viewmatrix[4][4];
|
|
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
|
|
const bool playing = stl->storage->is_playing;
|
|
const bool is_render = (bool)stl->storage->is_render;
|
|
const bool is_mat_preview = (bool)stl->storage->is_mat_preview;
|
|
const bool overlay_multiedit = v3d != NULL ? (v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) : true;
|
|
|
|
/* Get evaluation context */
|
|
/* NOTE: We must check if C is valid, otherwise we get crashes when trying to save files
|
|
* (i.e. the thumbnail offscreen rendering fails)
|
|
*/
|
|
Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
|
|
|
|
/* get parent matrix and save as static data */
|
|
ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, viewmatrix);
|
|
copy_m4_m4(derived_gpf->runtime.viewmatrix, viewmatrix);
|
|
|
|
if ((cache_ob != NULL) && (cache_ob->is_dup_ob)) {
|
|
copy_m4_m4(derived_gpf->runtime.viewmatrix, cache_ob->obmat);
|
|
}
|
|
|
|
/* apply geometry modifiers */
|
|
if ((cache->is_dirty) && (ob->greasepencil_modifiers.first) && (!is_multiedit)) {
|
|
if (!stl->storage->simplify_modif) {
|
|
if (BKE_gpencil_has_geometry_modifiers(ob)) {
|
|
BKE_gpencil_geometry_modifiers(depsgraph, ob, gpl, derived_gpf, stl->storage->is_render);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (src_gpf) {
|
|
src_gps = src_gpf->strokes.first;
|
|
}
|
|
else {
|
|
src_gps = NULL;
|
|
}
|
|
|
|
for (gps = derived_gpf->strokes.first; gps; gps = gps->next) {
|
|
MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
|
|
|
|
/* check if stroke can be drawn */
|
|
if (gpencil_can_draw_stroke(gp_style, gps, false, is_mat_preview) == false) {
|
|
continue;
|
|
}
|
|
/* limit the number of shading groups */
|
|
if (stl->storage->shgroup_id >= GPENCIL_MAX_SHGROUPS) {
|
|
continue;
|
|
}
|
|
|
|
/* be sure recalc all cache in source stroke to avoid recalculation when frame change
|
|
* and improve fps */
|
|
if (src_gps) {
|
|
DRW_gpencil_recalc_geometry_caches(ob, gp_style, src_gps);
|
|
}
|
|
|
|
/* if the fill has any value, it's considered a fill and is not drawn if simplify fill is enabled */
|
|
if ((stl->storage->simplify_fill) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_REMOVE_FILL_LINE)) {
|
|
if ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) ||
|
|
(gp_style->fill_style > GP_STYLE_FILL_STYLE_SOLID))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((gpl->actframe->framenum == derived_gpf->framenum) ||
|
|
(!is_multiedit) || (overlay_multiedit))
|
|
{
|
|
int id = stl->storage->shgroup_id;
|
|
if (gps->totpoints > 0) {
|
|
if ((gps->totpoints > 2) && (!stl->storage->simplify_fill) &&
|
|
((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) &&
|
|
((gps->flag & GP_STROKE_NOFILL) == 0))
|
|
{
|
|
stl->shgroups[id].shgrps_fill = DRW_gpencil_shgroup_fill_create(
|
|
e_data, vedata, psl->stroke_pass, e_data->gpencil_fill_sh, gpd, gp_style, id);
|
|
}
|
|
else {
|
|
stl->shgroups[id].shgrps_fill = NULL;
|
|
}
|
|
if ((gp_style->mode == GP_STYLE_MODE_LINE) && (gps->totpoints > 1)) {
|
|
stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_stroke_create(
|
|
e_data, vedata, psl->stroke_pass, e_data->gpencil_stroke_sh, ob, gpd, gp_style, id, false);
|
|
}
|
|
else {
|
|
stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_point_create(
|
|
e_data, vedata, psl->stroke_pass, e_data->gpencil_point_sh, ob, gpd, gp_style, id, false);
|
|
}
|
|
}
|
|
else {
|
|
stl->shgroups[id].shgrps_fill = NULL;
|
|
stl->shgroups[id].shgrps_stroke = NULL;
|
|
}
|
|
stl->storage->shgroup_id++;
|
|
|
|
fillgrp = stl->shgroups[id].shgrps_fill;
|
|
strokegrp = stl->shgroups[id].shgrps_stroke;
|
|
|
|
/* copy color to temp fields to apply temporal changes in the stroke */
|
|
copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba);
|
|
copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba);
|
|
|
|
/* apply modifiers (only modify geometry, but not create ) */
|
|
if ((cache->is_dirty) && (ob->greasepencil_modifiers.first) && (!is_multiedit)) {
|
|
if (!stl->storage->simplify_modif) {
|
|
BKE_gpencil_stroke_modifiers(depsgraph, ob, gpl, derived_gpf, gps, stl->storage->is_render);
|
|
}
|
|
}
|
|
|
|
/* fill */
|
|
if ((fillgrp) && (!stl->storage->simplify_fill)) {
|
|
gpencil_add_fill_shgroup(
|
|
cache, fillgrp, ob, derived_gpf, gps,
|
|
opacity, tintcolor, false, custonion);
|
|
}
|
|
/* stroke */
|
|
if (strokegrp) {
|
|
gpencil_add_stroke_shgroup(
|
|
cache, strokegrp, ob, gpl, derived_gpf, gps,
|
|
opacity, tintcolor, false, custonion);
|
|
}
|
|
}
|
|
|
|
/* edit points (only in edit mode and not play animation not render) */
|
|
if ((draw_ctx->obact == ob) && (src_gps) &&
|
|
(!playing) && (!is_render) && (!cache_ob->is_dup_ob))
|
|
{
|
|
if (!stl->g_data->shgrps_edit_line) {
|
|
stl->g_data->shgrps_edit_line = DRW_shgroup_create(e_data->gpencil_line_sh, psl->edit_pass);
|
|
}
|
|
if (!stl->g_data->shgrps_edit_point) {
|
|
stl->g_data->shgrps_edit_point = DRW_shgroup_create(e_data->gpencil_edit_point_sh, psl->edit_pass);
|
|
const float *viewport_size = DRW_viewport_size_get();
|
|
DRW_shgroup_uniform_vec2(stl->g_data->shgrps_edit_point, "Viewport", viewport_size, 1);
|
|
}
|
|
|
|
gpencil_add_editpoints_shgroup(stl, cache, ts, ob, gpd, gpl, derived_gpf, src_gps);
|
|
}
|
|
|
|
if (src_gps) {
|
|
src_gps = src_gps->next;
|
|
}
|
|
|
|
cache->cache_idx++;
|
|
}
|
|
}
|
|
|
|
/* draw stroke in drawing buffer */
|
|
void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, ToolSettings *ts, Object *ob)
|
|
{
|
|
GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
Brush *brush = BKE_brush_getactive_gpencil(ts);
|
|
bGPdata *gpd_eval = ob->data;
|
|
/* need the original to avoid cow overhead while drawing */
|
|
bGPdata *gpd = (bGPdata *)DEG_get_original_id(&gpd_eval->id);
|
|
|
|
MaterialGPencilStyle *gp_style = NULL;
|
|
|
|
float obscale = mat4_to_scale(ob->obmat);
|
|
|
|
/* use the brush material */
|
|
Material *ma = BKE_gpencil_get_material_from_brush(brush);
|
|
if (ma != NULL) {
|
|
gp_style = ma->gp_style;
|
|
}
|
|
/* this is not common, but avoid any special situations when brush could be without material */
|
|
if (gp_style == NULL) {
|
|
gp_style = BKE_material_gpencil_settings_get(ob, ob->actcol);
|
|
}
|
|
|
|
/* drawing strokes */
|
|
/* 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)
|
|
*/
|
|
if (ED_gpencil_session_active() && (gpd->runtime.sbuffer_size > 0)) {
|
|
if ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) {
|
|
/* It should also be noted that sbuffer contains temporary point types
|
|
* i.e. tGPspoints NOT bGPDspoints
|
|
*/
|
|
short lthick = brush->size * obscale;
|
|
/* if only one point, don't need to draw buffer because the user has no time to see it */
|
|
if (gpd->runtime.sbuffer_size > 1) {
|
|
if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
|
|
stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_stroke_create(
|
|
e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false);
|
|
}
|
|
else {
|
|
stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_point_create(
|
|
e_data, vedata, psl->drawing_pass, e_data->gpencil_point_sh, NULL, gpd, gp_style, -1, false);
|
|
}
|
|
|
|
/* clean previous version of the batch */
|
|
if (stl->storage->buffer_stroke) {
|
|
GPU_BATCH_DISCARD_SAFE(e_data->batch_buffer_stroke);
|
|
MEM_SAFE_FREE(e_data->batch_buffer_stroke);
|
|
stl->storage->buffer_stroke = false;
|
|
}
|
|
|
|
/* use unit matrix because the buffer is in screen space and does not need conversion */
|
|
if (gpd->runtime.mode == GP_STYLE_MODE_LINE) {
|
|
e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_stroke_geom(
|
|
gpd, lthick);
|
|
}
|
|
else {
|
|
e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_point_geom(
|
|
gpd, lthick);
|
|
}
|
|
|
|
DRW_shgroup_call_add(
|
|
stl->g_data->shgrps_drawing_stroke,
|
|
e_data->batch_buffer_stroke,
|
|
stl->storage->unit_matrix);
|
|
|
|
if ((gpd->runtime.sbuffer_size >= 3) && (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
|
|
((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0))
|
|
{
|
|
/* if not solid, fill is simulated with solid color */
|
|
if (gpd->runtime.bfill_style > 0) {
|
|
gpd->runtime.sfill[3] = 0.5f;
|
|
}
|
|
stl->g_data->shgrps_drawing_fill = DRW_shgroup_create(
|
|
e_data->gpencil_drawing_fill_sh, psl->drawing_pass);
|
|
|
|
/* clean previous version of the batch */
|
|
if (stl->storage->buffer_fill) {
|
|
GPU_BATCH_DISCARD_SAFE(e_data->batch_buffer_fill);
|
|
MEM_SAFE_FREE(e_data->batch_buffer_fill);
|
|
stl->storage->buffer_fill = false;
|
|
}
|
|
|
|
e_data->batch_buffer_fill = DRW_gpencil_get_buffer_fill_geom(gpd);
|
|
DRW_shgroup_call_add(
|
|
stl->g_data->shgrps_drawing_fill,
|
|
e_data->batch_buffer_fill,
|
|
stl->storage->unit_matrix);
|
|
stl->storage->buffer_fill = true;
|
|
}
|
|
stl->storage->buffer_stroke = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get alpha factor for onion strokes */
|
|
static void gpencil_get_onion_alpha(float color[4], bGPdata *gpd)
|
|
{
|
|
#define MIN_ALPHA_VALUE 0.01f
|
|
|
|
/* if fade is disabled, opacity is equal in all frames */
|
|
if ((gpd->onion_flag & GP_ONION_FADE) == 0) {
|
|
color[3] = gpd->onion_factor;
|
|
}
|
|
else {
|
|
/* add override opacity factor */
|
|
color[3] += gpd->onion_factor - 0.5f;
|
|
}
|
|
|
|
CLAMP(color[3], MIN_ALPHA_VALUE, 1.0f);
|
|
}
|
|
|
|
/* draw onion-skinning for a layer */
|
|
static void gpencil_draw_onionskins(
|
|
GpencilBatchCache *cache, GPENCIL_e_data *e_data, void *vedata,
|
|
Object *ob, bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf)
|
|
{
|
|
|
|
const float default_color[3] = { UNPACK3(U.gpencil_new_layer_col) };
|
|
const float alpha = 1.0f;
|
|
float color[4];
|
|
int idx;
|
|
float fac = 1.0f;
|
|
int step = 0;
|
|
int mode = 0;
|
|
bool colflag = false;
|
|
bGPDframe *gpf_loop = NULL;
|
|
int last = gpf->framenum;
|
|
|
|
colflag = (bool)gpd->onion_flag & GP_ONION_GHOST_PREVCOL;
|
|
|
|
|
|
/* -------------------------------
|
|
* 1) Draw Previous Frames First
|
|
* ------------------------------- */
|
|
step = gpd->gstep;
|
|
mode = gpd->onion_mode;
|
|
|
|
if (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) {
|
|
copy_v3_v3(color, gpd->gcolor_prev);
|
|
}
|
|
else {
|
|
copy_v3_v3(color, default_color);
|
|
}
|
|
|
|
idx = 0;
|
|
for (bGPDframe *gf = gpf->prev; gf; gf = gf->prev) {
|
|
/* only selected frames */
|
|
if ((mode == GP_ONION_MODE_SELECTED) && ((gf->flag & GP_FRAME_SELECT) == 0)) {
|
|
continue;
|
|
}
|
|
/* absolute range */
|
|
if (mode == GP_ONION_MODE_ABSOLUTE) {
|
|
if ((gpf->framenum - gf->framenum) > step) {
|
|
break;
|
|
}
|
|
}
|
|
/* relative range */
|
|
if (mode == GP_ONION_MODE_RELATIVE) {
|
|
idx++;
|
|
if (idx > step) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
/* alpha decreases with distance from curframe index */
|
|
if (mode != GP_ONION_MODE_SELECTED) {
|
|
if (mode == GP_ONION_MODE_ABSOLUTE) {
|
|
fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(step + 1));
|
|
}
|
|
else {
|
|
fac = 1.0f - ((float)idx / (float)(step + 1));
|
|
}
|
|
color[3] = alpha * fac * 0.66f;
|
|
}
|
|
else {
|
|
idx++;
|
|
fac = alpha - ((1.1f - (1.0f / (float)idx)) * 0.66f);
|
|
color[3] = fac;
|
|
}
|
|
|
|
/* if loop option, save the frame to use later */
|
|
if ((mode != GP_ONION_MODE_ABSOLUTE) && (gpd->onion_flag & GP_ONION_LOOP)) {
|
|
gpf_loop = gf;
|
|
}
|
|
|
|
gpencil_get_onion_alpha(color, gpd);
|
|
gpencil_draw_onion_strokes(cache, e_data, vedata, ob, gpd, gpl, gf, color[3], color, colflag);
|
|
}
|
|
/* -------------------------------
|
|
* 2) Now draw next frames
|
|
* ------------------------------- */
|
|
step = gpd->gstep_next;
|
|
mode = gpd->onion_mode;
|
|
|
|
if (gpd->onion_flag & GP_ONION_GHOST_NEXTCOL) {
|
|
copy_v3_v3(color, gpd->gcolor_next);
|
|
}
|
|
else {
|
|
copy_v3_v3(color, default_color);
|
|
}
|
|
|
|
idx = 0;
|
|
for (bGPDframe *gf = gpf->next; gf; gf = gf->next) {
|
|
/* only selected frames */
|
|
if ((mode == GP_ONION_MODE_SELECTED) && ((gf->flag & GP_FRAME_SELECT) == 0)) {
|
|
continue;
|
|
}
|
|
/* absolute range */
|
|
if (mode == GP_ONION_MODE_ABSOLUTE) {
|
|
if ((gf->framenum - gpf->framenum) > step) {
|
|
break;
|
|
}
|
|
}
|
|
/* relative range */
|
|
if (mode == GP_ONION_MODE_RELATIVE) {
|
|
idx++;
|
|
if (idx > step) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
/* alpha decreases with distance from curframe index */
|
|
if (mode != GP_ONION_MODE_SELECTED) {
|
|
if (mode == GP_ONION_MODE_ABSOLUTE) {
|
|
fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(step + 1));
|
|
}
|
|
else {
|
|
fac = 1.0f - ((float)idx / (float)(step + 1));
|
|
}
|
|
color[3] = alpha * fac * 0.66f;
|
|
}
|
|
else {
|
|
idx++;
|
|
fac = alpha - ((1.1f - (1.0f / (float)idx)) * 0.66f);
|
|
color[3] = fac;
|
|
}
|
|
|
|
gpencil_get_onion_alpha(color, gpd);
|
|
gpencil_draw_onion_strokes(cache, e_data, vedata, ob, gpd, gpl, gf, color[3], color, colflag);
|
|
if (last < gf->framenum) {
|
|
last = gf->framenum;
|
|
}
|
|
}
|
|
|
|
/* Draw first frame in blue for loop mode */
|
|
if ((gpd->onion_flag & GP_ONION_LOOP) && (gpf_loop != NULL)) {
|
|
if ((last == gpf->framenum) || (gpf->next == NULL)) {
|
|
gpencil_get_onion_alpha(color, gpd);
|
|
gpencil_draw_onion_strokes(
|
|
cache, e_data, vedata, ob, gpd, gpl,
|
|
gpf_loop, color[3], color, colflag);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* populate a datablock for multiedit (no onions, no modifiers) */
|
|
void DRW_gpencil_populate_multiedit(
|
|
GPENCIL_e_data *e_data, void *vedata, Scene *scene, Object *ob,
|
|
tGPencilObjectCache *cache_ob)
|
|
{
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
|
bGPDframe *gpf = NULL;
|
|
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
|
int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph);
|
|
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval);
|
|
ToolSettings *ts = scene->toolsettings;
|
|
cache->cache_idx = 0;
|
|
|
|
/* check if playing animation */
|
|
bool playing = stl->storage->is_playing;
|
|
|
|
/* draw strokes */
|
|
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
|
/* don't draw layer if hidden */
|
|
if (gpl->flag & GP_LAYER_HIDE)
|
|
continue;
|
|
|
|
/* list of frames to draw */
|
|
if (!playing) {
|
|
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
|
if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) {
|
|
gpencil_draw_strokes(
|
|
cache, e_data, vedata, ts, ob, gpd, gpl, gpf, gpf,
|
|
gpl->opacity, gpl->tintcolor, false, cache_ob);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
|
|
if (gpf) {
|
|
gpencil_draw_strokes(
|
|
cache, e_data, vedata, ts, ob, gpd, gpl, gpf, gpf,
|
|
gpl->opacity, gpl->tintcolor, false, cache_ob);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
cache->is_dirty = false;
|
|
}
|
|
|
|
/* helper for populate a complete grease pencil datablock */
|
|
void DRW_gpencil_populate_datablock(
|
|
GPENCIL_e_data *e_data, void *vedata,
|
|
Scene *scene, Object *ob,
|
|
tGPencilObjectCache *cache_ob)
|
|
{
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
|
View3D *v3d = draw_ctx->v3d;
|
|
int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph);
|
|
ToolSettings *ts = scene->toolsettings;
|
|
bGPDframe *derived_gpf = NULL;
|
|
const bool main_onion = v3d != NULL ? (v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) : true;
|
|
const bool do_onion = (bool)((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && main_onion;
|
|
const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) : true;
|
|
float opacity;
|
|
|
|
/* check if playing animation */
|
|
bool playing = stl->storage->is_playing;
|
|
|
|
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval);
|
|
cache->cache_idx = 0;
|
|
|
|
/* init general modifiers data */
|
|
if (!stl->storage->simplify_modif) {
|
|
if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) {
|
|
BKE_gpencil_lattice_init(ob);
|
|
}
|
|
}
|
|
/* draw normal strokes */
|
|
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
|
/* don't draw layer if hidden */
|
|
if (gpl->flag & GP_LAYER_HIDE)
|
|
continue;
|
|
|
|
bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
|
|
if (gpf == NULL)
|
|
continue;
|
|
|
|
/* if pose mode, maybe the overlay to fade geometry is enabled */
|
|
if ((draw_ctx->obact) && (draw_ctx->object_mode == OB_MODE_POSE) &&
|
|
(v3d->overlay.flag & V3D_OVERLAY_BONE_SELECT))
|
|
{
|
|
opacity = gpl->opacity * v3d->overlay.bone_select_alpha;
|
|
}
|
|
else {
|
|
opacity = gpl->opacity;
|
|
}
|
|
|
|
/* create GHash if need */
|
|
if (gpl->runtime.derived_data == NULL) {
|
|
gpl->runtime.derived_data = (GHash *)BLI_ghash_str_new(gpl->info);
|
|
}
|
|
|
|
if (BLI_ghash_haskey(gpl->runtime.derived_data, ob->id.name)) {
|
|
derived_gpf = BLI_ghash_lookup(gpl->runtime.derived_data, ob->id.name);
|
|
}
|
|
else {
|
|
/* verify we have frame duplicated already */
|
|
if (cache_ob->is_dup_ob) {
|
|
continue;
|
|
}
|
|
derived_gpf = NULL;
|
|
}
|
|
|
|
if (derived_gpf == NULL) {
|
|
cache->is_dirty = true;
|
|
}
|
|
if ((!cache_ob->is_dup_ob) && (cache->is_dirty)) {
|
|
if (derived_gpf != NULL) {
|
|
/* first clear temp data */
|
|
if (BLI_ghash_haskey(gpl->runtime.derived_data, ob->id.name)) {
|
|
BLI_ghash_remove(gpl->runtime.derived_data, ob->id.name, NULL, NULL);
|
|
}
|
|
|
|
BKE_gpencil_free_frame_runtime_data(derived_gpf);
|
|
}
|
|
/* create new data */
|
|
derived_gpf = BKE_gpencil_frame_duplicate(gpf);
|
|
if (!BLI_ghash_haskey(gpl->runtime.derived_data, ob->id.name)) {
|
|
BLI_ghash_insert(gpl->runtime.derived_data, ob->id.name, derived_gpf);
|
|
}
|
|
else {
|
|
BLI_ghash_reinsert(gpl->runtime.derived_data, ob->id.name, derived_gpf, NULL, NULL);
|
|
}
|
|
}
|
|
/* draw onion skins */
|
|
if (!ID_IS_LINKED(&gpd->id)) {
|
|
ID *orig_id = gpd->id.orig_id;
|
|
/* GPXX: Now only a datablock with one use is allowed to be compatible
|
|
* with instances
|
|
*/
|
|
if ((!cache_ob->is_dup_onion) && (gpd->flag & GP_DATA_SHOW_ONIONSKINS) &&
|
|
(do_onion) && (gpl->onion_flag & GP_LAYER_ONIONSKIN) &&
|
|
((!playing) || (gpd->onion_flag & GP_ONION_GHOST_ALWAYS)) &&
|
|
(!cache_ob->is_dup_ob) && (orig_id->us <= 1))
|
|
{
|
|
if (((!stl->storage->is_render) && (overlay)) ||
|
|
((stl->storage->is_render) && (gpd->onion_flag & GP_ONION_GHOST_ALWAYS)))
|
|
{
|
|
gpencil_draw_onionskins(cache, e_data, vedata, ob, gpd, gpl, gpf);
|
|
}
|
|
}
|
|
}
|
|
/* draw normal strokes */
|
|
if (!cache_ob->is_dup_ob) {
|
|
/* save batch index */
|
|
gpl->runtime.batch_index = cache->cache_idx;
|
|
}
|
|
else {
|
|
cache->cache_idx = gpl->runtime.batch_index;
|
|
}
|
|
|
|
gpencil_draw_strokes(
|
|
cache, e_data, vedata, ts, ob, gpd, gpl, gpf, derived_gpf,
|
|
opacity, gpl->tintcolor, false, cache_ob);
|
|
|
|
}
|
|
|
|
/* clear any lattice data */
|
|
if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) {
|
|
BKE_gpencil_lattice_clear(ob);
|
|
}
|
|
|
|
cache->is_dirty = false;
|
|
}
|