2018-07-31 10:22:19 +02:00
|
|
|
/*
|
|
|
|
|
* 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 */
|
2018-07-31 20:11:55 +10:00
|
|
|
static bool gpencil_can_draw_stroke(
|
|
|
|
|
struct MaterialGPencilStyle *gp_style, const bGPDstroke *gps,
|
|
|
|
|
const bool onion, const bool is_mat_preview)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
/* 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) ||
|
2018-07-31 20:11:55 +10:00
|
|
|
(gp_style->flag & GP_STYLE_COLOR_HIDE) ||
|
|
|
|
|
(onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN)))
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
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)) {
|
2018-09-09 11:41:25 +02:00
|
|
|
stl->shgroups[id].obj_scale = mat4_to_scale(ob->obmat);
|
2018-07-31 10:22:19 +02:00
|
|
|
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)) {
|
2018-09-09 11:41:25 +02:00
|
|
|
stl->shgroups[id].obj_scale = mat4_to_scale(ob->obmat);;
|
2018-07-31 10:22:19 +02:00
|
|
|
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,
|
2018-08-31 09:54:35 +02:00
|
|
|
Object *ob, bGPDframe *gpf, bGPDstroke *gps,
|
2018-08-30 17:48:53 -03:00
|
|
|
float opacity, const float tintcolor[4], const bool onion, const bool custonion)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
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]);
|
2018-08-30 12:22:55 +02:00
|
|
|
tfill[3] = gps->runtime.tmp_fill_rgba[3] * opacity;
|
2018-07-31 10:22:19 +02:00
|
|
|
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,
|
2018-08-15 16:07:16 +02:00
|
|
|
const float opacity, const float tintcolor[4], const bool onion,
|
|
|
|
|
const bool custonion)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
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)) {
|
2018-08-15 16:07:16 +02:00
|
|
|
cache->batch_stroke[cache->cache_idx] = DRW_gpencil_get_stroke_geom(gps, sthickness, ink);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
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) &&
|
2018-07-31 20:11:55 +10:00
|
|
|
((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) &&
|
2018-08-01 08:42:00 +10:00
|
|
|
(v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES))
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
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,
|
2018-08-15 16:07:16 +02:00
|
|
|
const float opacity, const float tintcolor[4],
|
2018-08-22 10:10:12 +10:00
|
|
|
const bool custonion, tGPencilObjectCache *cache_ob)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
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);
|
2018-08-07 13:56:30 +02:00
|
|
|
const bool playing = stl->storage->is_playing;
|
2018-07-31 10:22:19 +02:00
|
|
|
const bool is_render = (bool)stl->storage->is_render;
|
|
|
|
|
const bool is_mat_preview = (bool)stl->storage->is_mat_preview;
|
2018-08-01 08:42:00 +10:00
|
|
|
const bool overlay_multiedit = v3d != NULL ? (v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) : true;
|
2018-07-31 10:22:19 +02:00
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
2018-08-15 16:07:16 +02:00
|
|
|
if ((cache_ob != NULL) && (cache_ob->is_dup_ob)) {
|
|
|
|
|
copy_m4_m4(derived_gpf->runtime.viewmatrix, cache_ob->obmat);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-31 10:22:19 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-15 16:07:16 +02:00
|
|
|
/* be sure recalc all cache in source stroke to avoid recalculation when frame change
|
2018-07-31 10:22:19 +02:00
|
|
|
* 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(
|
2018-08-31 09:54:35 +02:00
|
|
|
cache, fillgrp, ob, derived_gpf, gps,
|
2018-09-02 16:45:00 +10:00
|
|
|
opacity, tintcolor, false, custonion);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
/* stroke */
|
|
|
|
|
if (strokegrp) {
|
|
|
|
|
gpencil_add_stroke_shgroup(
|
2018-08-15 16:07:16 +02:00
|
|
|
cache, strokegrp, ob, gpl, derived_gpf, gps,
|
2018-08-22 10:10:12 +10:00
|
|
|
opacity, tintcolor, false, custonion);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* edit points (only in edit mode and not play animation not render) */
|
2018-08-15 16:07:16 +02:00
|
|
|
if ((draw_ctx->obact == ob) && (src_gps) &&
|
2018-08-22 10:10:12 +10:00
|
|
|
(!playing) && (!is_render) && (!cache_ob->is_dup_ob))
|
2018-08-15 16:07:16 +02:00
|
|
|
{
|
2018-07-31 10:22:19 +02:00
|
|
|
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);
|
2018-09-10 20:24:05 +02:00
|
|
|
bGPdata *gpd_eval = ob->data;
|
|
|
|
|
/* need the original to avoid cow overhead while drawing */
|
|
|
|
|
bGPdata *gpd = (bGPdata *)DEG_get_original_id(&gpd_eval->id);
|
|
|
|
|
|
2018-07-31 10:22:19 +02:00
|
|
|
MaterialGPencilStyle *gp_style = NULL;
|
|
|
|
|
|
2018-09-09 11:48:02 +02:00
|
|
|
float obscale = mat4_to_scale(ob->obmat);
|
2018-07-31 10:22:19 +02:00
|
|
|
|
|
|
|
|
/* 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
|
2018-09-02 18:28:27 +10:00
|
|
|
* i.e. tGPspoints NOT bGPDspoints
|
|
|
|
|
*/
|
2018-07-31 10:22:19 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-08 19:48:57 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-31 10:22:19 +02:00
|
|
|
/* use unit matrix because the buffer is in screen space and does not need conversion */
|
|
|
|
|
if (gpd->runtime.mode == GP_STYLE_MODE_LINE) {
|
2018-08-08 19:48:57 +02:00
|
|
|
e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_stroke_geom(
|
2018-08-15 16:07:16 +02:00
|
|
|
gpd, lthick);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2018-08-08 19:48:57 +02:00
|
|
|
e_data->batch_buffer_stroke = DRW_gpencil_get_buffer_point_geom(
|
2018-08-15 16:07:16 +02:00
|
|
|
gpd, lthick);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DRW_shgroup_call_add(
|
|
|
|
|
stl->g_data->shgrps_drawing_stroke,
|
2018-08-08 19:48:57 +02:00
|
|
|
e_data->batch_buffer_stroke,
|
2018-07-31 10:22:19 +02:00
|
|
|
stl->storage->unit_matrix);
|
|
|
|
|
|
|
|
|
|
if ((gpd->runtime.sbuffer_size >= 3) && (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
|
2018-07-31 20:11:55 +10:00
|
|
|
((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0))
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
/* 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);
|
2018-08-08 19:48:57 +02:00
|
|
|
|
|
|
|
|
/* 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);
|
2018-07-31 10:22:19 +02:00
|
|
|
DRW_shgroup_call_add(
|
|
|
|
|
stl->g_data->shgrps_drawing_fill,
|
2018-08-08 19:48:57 +02:00
|
|
|
e_data->batch_buffer_fill,
|
2018-07-31 10:22:19 +02:00
|
|
|
stl->storage->unit_matrix);
|
2018-08-08 19:48:57 +02:00
|
|
|
stl->storage->buffer_fill = true;
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
2018-08-08 19:48:57 +02:00
|
|
|
stl->storage->buffer_stroke = true;
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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) */
|
2018-08-22 10:10:12 +10:00
|
|
|
void DRW_gpencil_populate_multiedit(
|
|
|
|
|
GPENCIL_e_data *e_data, void *vedata, Scene *scene, Object *ob,
|
|
|
|
|
tGPencilObjectCache *cache_ob)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
2018-08-15 16:07:16 +02:00
|
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
2018-07-31 10:22:19 +02:00
|
|
|
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 */
|
2018-08-07 13:56:30 +02:00
|
|
|
bool playing = stl->storage->is_playing;
|
2018-07-31 10:22:19 +02:00
|
|
|
|
|
|
|
|
/* 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,
|
2018-08-15 16:07:16 +02:00
|
|
|
gpl->opacity, gpl->tintcolor, false, cache_ob);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2018-09-03 10:14:13 +02:00
|
|
|
gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
|
2018-07-31 10:22:19 +02:00
|
|
|
if (gpf) {
|
|
|
|
|
gpencil_draw_strokes(
|
|
|
|
|
cache, e_data, vedata, ts, ob, gpd, gpl, gpf, gpf,
|
2018-08-15 16:07:16 +02:00
|
|
|
gpl->opacity, gpl->tintcolor, false, cache_ob);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cache->is_dirty = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* helper for populate a complete grease pencil datablock */
|
2018-08-15 16:07:16 +02:00
|
|
|
void DRW_gpencil_populate_datablock(
|
2018-08-22 10:10:12 +10:00
|
|
|
GPENCIL_e_data *e_data, void *vedata,
|
|
|
|
|
Scene *scene, Object *ob,
|
|
|
|
|
tGPencilObjectCache *cache_ob)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
|
|
|
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
2018-08-15 16:07:16 +02:00
|
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
2018-07-31 10:22:19 +02:00
|
|
|
View3D *v3d = draw_ctx->v3d;
|
|
|
|
|
int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph);
|
|
|
|
|
ToolSettings *ts = scene->toolsettings;
|
|
|
|
|
bGPDframe *derived_gpf = NULL;
|
2018-08-15 16:07:16 +02:00
|
|
|
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;
|
2018-08-11 20:54:13 +02:00
|
|
|
const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) : true;
|
2018-08-30 12:22:55 +02:00
|
|
|
float opacity;
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
/* check if playing animation */
|
|
|
|
|
bool playing = stl->storage->is_playing;
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval);
|
|
|
|
|
cache->cache_idx = 0;
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
/* init general modifiers data */
|
|
|
|
|
if (!stl->storage->simplify_modif) {
|
|
|
|
|
if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) {
|
|
|
|
|
BKE_gpencil_lattice_init(ob);
|
|
|
|
|
}
|
2018-08-10 21:32:25 +02:00
|
|
|
}
|
2018-08-11 20:54:13 +02:00
|
|
|
/* 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;
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-09-03 10:14:13 +02:00
|
|
|
bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
|
2018-08-11 20:54:13 +02:00
|
|
|
if (gpf == NULL)
|
|
|
|
|
continue;
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-08-30 12:22:55 +02:00
|
|
|
/* if pose mode, maybe the overlay to fade geometry is enabled */
|
|
|
|
|
if ((draw_ctx->obact) && (draw_ctx->object_mode == OB_MODE_POSE) &&
|
2018-09-02 16:45:00 +10:00
|
|
|
(v3d->overlay.flag & V3D_OVERLAY_BONE_SELECT))
|
2018-08-30 12:22:55 +02:00
|
|
|
{
|
|
|
|
|
opacity = gpl->opacity * v3d->overlay.bone_select_alpha;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
opacity = gpl->opacity;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
/* create GHash if need */
|
|
|
|
|
if (gpl->runtime.derived_data == NULL) {
|
|
|
|
|
gpl->runtime.derived_data = (GHash *)BLI_ghash_str_new(gpl->info);
|
2018-08-09 20:48:05 +02:00
|
|
|
}
|
2018-08-11 20:54:13 +02:00
|
|
|
|
2018-08-22 10:10:12 +10:00
|
|
|
if (BLI_ghash_haskey(gpl->runtime.derived_data, ob->id.name)) {
|
2018-08-11 20:54:13 +02:00
|
|
|
derived_gpf = BLI_ghash_lookup(gpl->runtime.derived_data, ob->id.name);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
2018-08-10 21:32:25 +02:00
|
|
|
else {
|
2018-08-15 16:07:16 +02:00
|
|
|
/* verify we have frame duplicated already */
|
|
|
|
|
if (cache_ob->is_dup_ob) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-08-11 20:54:13 +02:00
|
|
|
derived_gpf = NULL;
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
if (derived_gpf == NULL) {
|
|
|
|
|
cache->is_dirty = true;
|
|
|
|
|
}
|
2018-08-15 16:07:16 +02:00
|
|
|
if ((!cache_ob->is_dup_ob) && (cache->is_dirty)) {
|
2018-08-11 20:54:13 +02:00
|
|
|
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 */
|
2018-08-15 16:07:16 +02:00
|
|
|
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
|
2018-09-02 18:28:27 +10:00
|
|
|
* with instances
|
|
|
|
|
*/
|
2018-08-15 16:07:16 +02:00
|
|
|
if ((!cache_ob->is_dup_onion) && (gpd->flag & GP_DATA_SHOW_ONIONSKINS) &&
|
2018-08-22 10:10:12 +10:00
|
|
|
(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))
|
2018-08-11 20:54:13 +02:00
|
|
|
{
|
2018-08-15 16:07:16 +02:00
|
|
|
if (((!stl->storage->is_render) && (overlay)) ||
|
2018-08-22 10:10:12 +10:00
|
|
|
((stl->storage->is_render) && (gpd->onion_flag & GP_ONION_GHOST_ALWAYS)))
|
2018-08-15 16:07:16 +02:00
|
|
|
{
|
|
|
|
|
gpencil_draw_onionskins(cache, e_data, vedata, ob, gpd, gpl, gpf);
|
|
|
|
|
}
|
2018-08-11 20:54:13 +02:00
|
|
|
}
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
2018-08-11 20:54:13 +02:00
|
|
|
/* draw normal strokes */
|
2018-08-15 16:07:16 +02:00
|
|
|
if (!cache_ob->is_dup_ob) {
|
|
|
|
|
/* save batch index */
|
|
|
|
|
gpl->runtime.batch_index = cache->cache_idx;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cache->cache_idx = gpl->runtime.batch_index;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
gpencil_draw_strokes(
|
|
|
|
|
cache, e_data, vedata, ts, ob, gpd, gpl, gpf, derived_gpf,
|
2018-08-30 12:22:55 +02:00
|
|
|
opacity, gpl->tintcolor, false, cache_ob);
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
}
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
/* clear any lattice data */
|
|
|
|
|
if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) {
|
|
|
|
|
BKE_gpencil_lattice_clear(ob);
|
|
|
|
|
}
|
2018-07-31 10:22:19 +02:00
|
|
|
|
2018-08-11 20:54:13 +02:00
|
|
|
cache->is_dirty = false;
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|