2012-01-19 02:06:09 +00:00
|
|
|
/*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
*
|
|
|
|
|
* The Original Code is Copyright (C) 2009 by Nicholas Bishop
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup edsculpt
|
2012-01-19 02:06:09 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
#include "BLI_math.h"
|
2012-08-21 20:34:05 +00:00
|
|
|
#include "BLI_rect.h"
|
2016-01-17 17:16:57 +01:00
|
|
|
#include "BLI_task.h"
|
2012-01-19 02:06:09 +00:00
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
|
|
|
|
|
#include "DNA_brush_types.h"
|
2013-05-30 02:16:22 +00:00
|
|
|
#include "DNA_customdata_types.h"
|
2012-01-19 02:06:09 +00:00
|
|
|
#include "DNA_color_types.h"
|
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
|
#include "DNA_screen_types.h"
|
2018-10-25 16:06:47 +11:00
|
|
|
#include "DNA_space_types.h"
|
2012-01-19 02:06:09 +00:00
|
|
|
#include "DNA_userdef_types.h"
|
2017-10-05 17:35:46 +11:00
|
|
|
#include "DNA_view3d_types.h"
|
2012-01-19 02:06:09 +00:00
|
|
|
|
|
|
|
|
#include "BKE_brush.h"
|
|
|
|
|
#include "BKE_context.h"
|
2014-07-21 12:02:05 +02:00
|
|
|
#include "BKE_curve.h"
|
2013-01-21 08:49:42 +00:00
|
|
|
#include "BKE_image.h"
|
2014-03-27 12:09:50 +02:00
|
|
|
#include "BKE_node.h"
|
2012-01-19 02:06:09 +00:00
|
|
|
#include "BKE_paint.h"
|
2013-04-12 21:58:18 +00:00
|
|
|
#include "BKE_colortools.h"
|
2012-01-19 02:06:09 +00:00
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
|
|
2014-04-13 17:20:06 +03:00
|
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
|
|
2012-01-19 02:06:09 +00:00
|
|
|
#include "ED_view3d.h"
|
|
|
|
|
|
2017-04-05 13:01:32 +02:00
|
|
|
#include "GPU_draw.h"
|
2017-03-03 17:21:34 -05:00
|
|
|
#include "GPU_immediate.h"
|
2017-04-05 18:30:14 +10:00
|
|
|
#include "GPU_immediate_util.h"
|
2017-03-21 00:09:40 -04:00
|
|
|
#include "GPU_matrix.h"
|
2018-06-27 19:07:23 -06:00
|
|
|
#include "GPU_state.h"
|
2015-11-28 01:20:28 +01:00
|
|
|
|
2014-07-21 12:02:05 +02:00
|
|
|
#include "UI_resources.h"
|
|
|
|
|
|
2012-01-19 02:06:09 +00:00
|
|
|
#include "paint_intern.h"
|
|
|
|
|
/* still needed for sculpt_stroke_get_location, should be
|
2012-03-03 16:31:46 +00:00
|
|
|
* removed eventually (TODO) */
|
2012-01-19 02:06:09 +00:00
|
|
|
#include "sculpt_intern.h"
|
|
|
|
|
|
|
|
|
|
/* TODOs:
|
2012-03-03 16:31:46 +00:00
|
|
|
*
|
|
|
|
|
* Some of the cursor drawing code is doing non-draw stuff
|
|
|
|
|
* (e.g. updating the brush rake angle). This should be cleaned up
|
|
|
|
|
* still.
|
|
|
|
|
*
|
|
|
|
|
* There is also some ugliness with sculpt-specific code.
|
2012-01-19 02:06:09 +00:00
|
|
|
*/
|
|
|
|
|
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
typedef struct TexSnapshot {
|
2019-04-17 06:17:24 +02:00
|
|
|
GLuint overlay_texture;
|
|
|
|
|
int winx;
|
|
|
|
|
int winy;
|
|
|
|
|
int old_size;
|
|
|
|
|
float old_zoom;
|
|
|
|
|
bool old_col;
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
} TexSnapshot;
|
|
|
|
|
|
2013-10-09 16:00:15 +00:00
|
|
|
typedef struct CursorSnapshot {
|
2019-04-17 06:17:24 +02:00
|
|
|
GLuint overlay_texture;
|
|
|
|
|
int size;
|
|
|
|
|
int zoom;
|
2013-10-09 16:00:15 +00:00
|
|
|
} CursorSnapshot;
|
|
|
|
|
|
|
|
|
|
static TexSnapshot primary_snap = {0};
|
2019-04-17 06:17:24 +02:00
|
|
|
static TexSnapshot secondary_snap = {0};
|
|
|
|
|
static CursorSnapshot cursor_snap = {0};
|
2013-10-09 16:00:15 +00:00
|
|
|
|
|
|
|
|
/* delete overlay cursor textures to preserve memory and invalidate all overlay flags */
|
2013-10-10 17:28:01 +00:00
|
|
|
void paint_cursor_delete_textures(void)
|
2013-10-09 16:00:15 +00:00
|
|
|
{
|
2019-04-22 09:19:45 +10:00
|
|
|
if (primary_snap.overlay_texture) {
|
2019-04-17 06:17:24 +02:00
|
|
|
glDeleteTextures(1, &primary_snap.overlay_texture);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
if (secondary_snap.overlay_texture) {
|
2019-04-17 06:17:24 +02:00
|
|
|
glDeleteTextures(1, &secondary_snap.overlay_texture);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
if (cursor_snap.overlay_texture) {
|
2019-04-17 06:17:24 +02:00
|
|
|
glDeleteTextures(1, &cursor_snap.overlay_texture);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
memset(&primary_snap, 0, sizeof(TexSnapshot));
|
|
|
|
|
memset(&secondary_snap, 0, sizeof(TexSnapshot));
|
|
|
|
|
memset(&cursor_snap, 0, sizeof(CursorSnapshot));
|
|
|
|
|
|
|
|
|
|
BKE_paint_invalidate_overlay_all();
|
2013-10-09 16:00:15 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-22 20:46:18 +00:00
|
|
|
static int same_tex_snap(TexSnapshot *snap, MTex *mtex, ViewContext *vc, bool col, float zoom)
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
return (/* make brush smaller shouldn't cause a resample */
|
|
|
|
|
//(mtex->brush_map_mode != MTEX_MAP_MODE_VIEW ||
|
|
|
|
|
//(BKE_brush_size_get(vc->scene, brush) <= snap->BKE_brush_size_get)) &&
|
|
|
|
|
|
|
|
|
|
(mtex->brush_map_mode != MTEX_MAP_MODE_TILED ||
|
|
|
|
|
(vc->ar->winx == snap->winx && vc->ar->winy == snap->winy)) &&
|
|
|
|
|
(mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL || snap->old_zoom == zoom) &&
|
|
|
|
|
snap->old_col == col);
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-22 20:46:18 +00:00
|
|
|
static void make_tex_snap(TexSnapshot *snap, ViewContext *vc, float zoom)
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
snap->old_zoom = zoom;
|
|
|
|
|
snap->winx = vc->ar->winx;
|
|
|
|
|
snap->winy = vc->ar->winy;
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
2016-01-17 17:16:57 +01:00
|
|
|
typedef struct LoadTexData {
|
2019-04-17 06:17:24 +02:00
|
|
|
Brush *br;
|
|
|
|
|
ViewContext *vc;
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
MTex *mtex;
|
|
|
|
|
GLubyte *buffer;
|
|
|
|
|
bool col;
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
struct ImagePool *pool;
|
|
|
|
|
int size;
|
|
|
|
|
float rotation;
|
|
|
|
|
float radius;
|
2016-01-17 17:16:57 +01:00
|
|
|
} LoadTexData;
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static void load_tex_task_cb_ex(void *__restrict userdata,
|
|
|
|
|
const int j,
|
|
|
|
|
const ParallelRangeTLS *__restrict tls)
|
2016-01-17 17:16:57 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
LoadTexData *data = userdata;
|
|
|
|
|
Brush *br = data->br;
|
|
|
|
|
ViewContext *vc = data->vc;
|
|
|
|
|
|
|
|
|
|
MTex *mtex = data->mtex;
|
|
|
|
|
GLubyte *buffer = data->buffer;
|
|
|
|
|
const bool col = data->col;
|
|
|
|
|
|
|
|
|
|
struct ImagePool *pool = data->pool;
|
|
|
|
|
const int size = data->size;
|
|
|
|
|
const float rotation = data->rotation;
|
|
|
|
|
const float radius = data->radius;
|
|
|
|
|
|
|
|
|
|
bool convert_to_linear = false;
|
|
|
|
|
struct ColorSpace *colorspace = NULL;
|
|
|
|
|
|
|
|
|
|
if (mtex->tex && mtex->tex->type == TEX_IMAGE && mtex->tex->ima) {
|
|
|
|
|
ImBuf *tex_ibuf = BKE_image_pool_acquire_ibuf(mtex->tex->ima, &mtex->tex->iuser, pool);
|
|
|
|
|
/* For consistency, sampling always returns color in linear space */
|
|
|
|
|
if (tex_ibuf && tex_ibuf->rect_float == NULL) {
|
|
|
|
|
convert_to_linear = true;
|
|
|
|
|
colorspace = tex_ibuf->rect_colorspace;
|
|
|
|
|
}
|
|
|
|
|
BKE_image_pool_release_ibuf(mtex->tex->ima, tex_ibuf, pool);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
|
// largely duplicated from tex_strength
|
|
|
|
|
|
|
|
|
|
int index = j * size + i;
|
|
|
|
|
|
|
|
|
|
float x = (float)i / size;
|
|
|
|
|
float y = (float)j / size;
|
|
|
|
|
float len;
|
|
|
|
|
|
|
|
|
|
if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
|
|
|
|
|
x *= vc->ar->winx / radius;
|
|
|
|
|
y *= vc->ar->winy / radius;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
x = (x - 0.5f) * 2.0f;
|
|
|
|
|
y = (y - 0.5f) * 2.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = sqrtf(x * x + y * y);
|
|
|
|
|
|
|
|
|
|
if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL) || len <= 1.0f) {
|
2019-04-22 00:18:34 +10:00
|
|
|
/* It is probably worth optimizing for those cases where the texture is not rotated by
|
|
|
|
|
* skipping the calls to atan2, sqrtf, sin, and cos. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (mtex->tex && (rotation > 0.001f || rotation < -0.001f)) {
|
|
|
|
|
const float angle = atan2f(y, x) + rotation;
|
|
|
|
|
|
|
|
|
|
x = len * cosf(angle);
|
|
|
|
|
y = len * sinf(angle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (col) {
|
|
|
|
|
float rgba[4];
|
|
|
|
|
|
|
|
|
|
paint_get_tex_pixel_col(
|
|
|
|
|
mtex, x, y, rgba, pool, tls->thread_id, convert_to_linear, colorspace);
|
|
|
|
|
|
|
|
|
|
buffer[index * 4] = rgba[0] * 255;
|
|
|
|
|
buffer[index * 4 + 1] = rgba[1] * 255;
|
|
|
|
|
buffer[index * 4 + 2] = rgba[2] * 255;
|
|
|
|
|
buffer[index * 4 + 3] = rgba[3] * 255;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
float avg = paint_get_tex_pixel(mtex, x, y, pool, tls->thread_id);
|
|
|
|
|
|
|
|
|
|
avg += br->texture_sample_bias;
|
|
|
|
|
|
|
|
|
|
/* clamp to avoid precision overflow */
|
|
|
|
|
CLAMP(avg, 0.0f, 1.0f);
|
|
|
|
|
buffer[index] = 255 - (GLubyte)(255 * avg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (col) {
|
|
|
|
|
buffer[index * 4] = 0;
|
|
|
|
|
buffer[index * 4 + 1] = 0;
|
|
|
|
|
buffer[index * 4 + 2] = 0;
|
|
|
|
|
buffer[index * 4 + 3] = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
buffer[index] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-17 17:16:57 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-22 20:46:18 +00:00
|
|
|
static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool primary)
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bool init;
|
|
|
|
|
TexSnapshot *target;
|
|
|
|
|
|
|
|
|
|
MTex *mtex = (primary) ? &br->mtex : &br->mask_mtex;
|
|
|
|
|
eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags();
|
|
|
|
|
GLubyte *buffer = NULL;
|
|
|
|
|
|
|
|
|
|
int size;
|
|
|
|
|
bool refresh;
|
|
|
|
|
eOverlayControlFlags invalid = ((primary) ?
|
|
|
|
|
(overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY) :
|
|
|
|
|
(overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY));
|
|
|
|
|
target = (primary) ? &primary_snap : &secondary_snap;
|
|
|
|
|
|
|
|
|
|
refresh = !target->overlay_texture || (invalid != 0) ||
|
|
|
|
|
!same_tex_snap(target, mtex, vc, col, zoom);
|
|
|
|
|
|
|
|
|
|
init = (target->overlay_texture != 0);
|
|
|
|
|
|
|
|
|
|
if (refresh) {
|
|
|
|
|
struct ImagePool *pool = NULL;
|
|
|
|
|
/* stencil is rotated later */
|
|
|
|
|
const float rotation = (mtex->brush_map_mode != MTEX_MAP_MODE_STENCIL) ? -mtex->rot : 0.0f;
|
|
|
|
|
const float radius = BKE_brush_size_get(vc->scene, br) * zoom;
|
|
|
|
|
|
|
|
|
|
make_tex_snap(target, vc, zoom);
|
|
|
|
|
|
|
|
|
|
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
|
|
|
|
|
int s = BKE_brush_size_get(vc->scene, br);
|
|
|
|
|
int r = 1;
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
for (s >>= 1; s > 0; s >>= 1) {
|
2019-04-17 06:17:24 +02:00
|
|
|
r++;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
size = (1 << r);
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (size < 256) {
|
2019-04-17 06:17:24 +02:00
|
|
|
size = 256;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (size < target->old_size) {
|
2019-04-17 06:17:24 +02:00
|
|
|
size = target->old_size;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
size = 512;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (target->old_size != size) {
|
|
|
|
|
if (target->overlay_texture) {
|
|
|
|
|
glDeleteTextures(1, &target->overlay_texture);
|
|
|
|
|
target->overlay_texture = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init = false;
|
|
|
|
|
|
|
|
|
|
target->old_size = size;
|
|
|
|
|
}
|
2019-04-22 09:19:45 +10:00
|
|
|
if (col) {
|
2019-04-17 06:17:24 +02:00
|
|
|
buffer = MEM_mallocN(sizeof(GLubyte) * size * size * 4, "load_tex");
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex");
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
pool = BKE_image_pool_new();
|
|
|
|
|
|
|
|
|
|
if (mtex->tex && mtex->tex->nodetree) {
|
|
|
|
|
/* has internal flag to detect it only does it once */
|
|
|
|
|
ntreeTexBeginExecTree(mtex->tex->nodetree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LoadTexData data = {
|
|
|
|
|
.br = br,
|
|
|
|
|
.vc = vc,
|
|
|
|
|
.mtex = mtex,
|
|
|
|
|
.buffer = buffer,
|
|
|
|
|
.col = col,
|
|
|
|
|
.pool = pool,
|
|
|
|
|
.size = size,
|
|
|
|
|
.rotation = rotation,
|
|
|
|
|
.radius = radius,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ParallelRangeSettings settings;
|
|
|
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
|
|
|
BLI_task_parallel_range(0, size, &data, load_tex_task_cb_ex, &settings);
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (mtex->tex && mtex->tex->nodetree) {
|
2019-04-17 06:17:24 +02:00
|
|
|
ntreeTexEndExecTree(mtex->tex->nodetree->execdata);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (pool) {
|
2019-04-17 06:17:24 +02:00
|
|
|
BKE_image_pool_free(pool);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!target->overlay_texture) {
|
2019-04-17 06:17:24 +02:00
|
|
|
glGenTextures(1, &target->overlay_texture);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
size = target->old_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, target->overlay_texture);
|
|
|
|
|
|
|
|
|
|
if (refresh) {
|
|
|
|
|
GLenum format = col ? GL_RGBA : GL_RED;
|
|
|
|
|
GLenum internalformat = col ? GL_RGBA8 : GL_R8;
|
|
|
|
|
|
|
|
|
|
if (!init || (target->old_col != col)) {
|
|
|
|
|
glTexImage2D(
|
|
|
|
|
GL_TEXTURE_2D, 0, internalformat, size, size, 0, format, GL_UNSIGNED_BYTE, buffer);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, format, GL_UNSIGNED_BYTE, buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (buffer) {
|
2019-04-17 06:17:24 +02:00
|
|
|
MEM_freeN(buffer);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
target->old_col = col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
|
|
|
|
|
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_paint_reset_overlay_invalid(invalid);
|
|
|
|
|
|
|
|
|
|
return 1;
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static void load_tex_cursor_task_cb(void *__restrict userdata,
|
|
|
|
|
const int j,
|
|
|
|
|
const ParallelRangeTLS *__restrict UNUSED(tls))
|
2016-01-17 17:16:57 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
LoadTexData *data = userdata;
|
|
|
|
|
Brush *br = data->br;
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
GLubyte *buffer = data->buffer;
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
const int size = data->size;
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
|
// largely duplicated from tex_strength
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
const int index = j * size + i;
|
|
|
|
|
const float x = (((float)i / size) - 0.5f) * 2.0f;
|
|
|
|
|
const float y = (((float)j / size) - 0.5f) * 2.0f;
|
|
|
|
|
const float len = sqrtf(x * x + y * y);
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (len <= 1.0f) {
|
|
|
|
|
float avg = BKE_brush_curve_strength_clamped(br, len, 1.0f); /* Falloff curve */
|
2016-01-17 17:16:57 +01:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
buffer[index] = 255 - (GLubyte)(255 * avg);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
buffer[index] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-17 17:16:57 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-22 20:46:18 +00:00
|
|
|
static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom)
|
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
bool init;
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags();
|
|
|
|
|
GLubyte *buffer = NULL;
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
int size;
|
|
|
|
|
const bool refresh = !cursor_snap.overlay_texture ||
|
|
|
|
|
(overlay_flags & PAINT_OVERLAY_INVALID_CURVE) || cursor_snap.zoom != zoom;
|
2013-10-09 16:00:15 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
init = (cursor_snap.overlay_texture != 0);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (refresh) {
|
|
|
|
|
int s, r;
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
cursor_snap.zoom = zoom;
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
s = BKE_brush_size_get(vc->scene, br);
|
|
|
|
|
r = 1;
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
for (s >>= 1; s > 0; s >>= 1) {
|
2019-04-17 06:17:24 +02:00
|
|
|
r++;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
size = (1 << r);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (size < 256) {
|
2019-04-17 06:17:24 +02:00
|
|
|
size = 256;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (size < cursor_snap.size) {
|
2019-04-17 06:17:24 +02:00
|
|
|
size = cursor_snap.size;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (cursor_snap.size != size) {
|
|
|
|
|
if (cursor_snap.overlay_texture) {
|
|
|
|
|
glDeleteTextures(1, &cursor_snap.overlay_texture);
|
|
|
|
|
cursor_snap.overlay_texture = 0;
|
|
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
init = false;
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
cursor_snap.size = size;
|
|
|
|
|
}
|
|
|
|
|
buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex");
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
curvemapping_initialize(br->curve);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
LoadTexData data = {
|
|
|
|
|
.br = br,
|
|
|
|
|
.buffer = buffer,
|
|
|
|
|
.size = size,
|
|
|
|
|
};
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
ParallelRangeSettings settings;
|
|
|
|
|
BLI_parallel_range_settings_defaults(&settings);
|
|
|
|
|
BLI_task_parallel_range(0, size, &data, load_tex_cursor_task_cb, &settings);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!cursor_snap.overlay_texture) {
|
2019-04-17 06:17:24 +02:00
|
|
|
glGenTextures(1, &cursor_snap.overlay_texture);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
size = cursor_snap.size;
|
|
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, cursor_snap.overlay_texture);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (refresh) {
|
|
|
|
|
if (!init) {
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, size, size, 0, GL_RED, GL_UNSIGNED_BYTE, buffer);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_RED, GL_UNSIGNED_BYTE, buffer);
|
|
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (buffer) {
|
2019-04-17 06:17:24 +02:00
|
|
|
MEM_freeN(buffer);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
BKE_paint_reset_overlay_invalid(PAINT_OVERLAY_INVALID_CURVE);
|
2013-04-22 20:46:18 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return 1;
|
2013-04-22 20:46:18 +00:00
|
|
|
}
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static int project_brush_radius(ViewContext *vc, float radius, const float location[3])
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2];
|
|
|
|
|
|
|
|
|
|
ED_view3d_global_to_vector(vc->rv3d, location, view);
|
|
|
|
|
|
|
|
|
|
/* create a vector that is not orthogonal to view */
|
|
|
|
|
|
|
|
|
|
if (fabsf(view[0]) < 0.1f) {
|
|
|
|
|
nonortho[0] = view[0] + 1.0f;
|
|
|
|
|
nonortho[1] = view[1];
|
|
|
|
|
nonortho[2] = view[2];
|
|
|
|
|
}
|
|
|
|
|
else if (fabsf(view[1]) < 0.1f) {
|
|
|
|
|
nonortho[0] = view[0];
|
|
|
|
|
nonortho[1] = view[1] + 1.0f;
|
|
|
|
|
nonortho[2] = view[2];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nonortho[0] = view[0];
|
|
|
|
|
nonortho[1] = view[1];
|
|
|
|
|
nonortho[2] = view[2] + 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get a vector in the plane of the view */
|
|
|
|
|
cross_v3_v3v3(ortho, nonortho, view);
|
|
|
|
|
normalize_v3(ortho);
|
|
|
|
|
|
|
|
|
|
/* make a point on the surface of the brush tangent to the view */
|
|
|
|
|
mul_v3_fl(ortho, radius);
|
|
|
|
|
add_v3_v3v3(offset, location, ortho);
|
|
|
|
|
|
|
|
|
|
/* project the center of the brush, and the tangent point to the view onto the screen */
|
|
|
|
|
if ((ED_view3d_project_float_global(vc->ar, location, p1, V3D_PROJ_TEST_NOP) ==
|
|
|
|
|
V3D_PROJ_RET_OK) &&
|
|
|
|
|
(ED_view3d_project_float_global(vc->ar, offset, p2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK)) {
|
|
|
|
|
/* the distance between these points is the size of the projected brush in pixels */
|
|
|
|
|
return len_v2v2(p1, p2);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BLI_assert(0); /* assert because the code that sets up the vectors should disallow this */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static bool sculpt_get_brush_geometry(bContext *C,
|
|
|
|
|
ViewContext *vc,
|
|
|
|
|
int x,
|
|
|
|
|
int y,
|
|
|
|
|
int *pixel_radius,
|
|
|
|
|
float location[3],
|
|
|
|
|
UnifiedPaintSettings *ups)
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
Paint *paint = BKE_paint_get_active_from_context(C);
|
|
|
|
|
float mouse[2];
|
|
|
|
|
bool hit = false;
|
|
|
|
|
|
|
|
|
|
mouse[0] = x;
|
|
|
|
|
mouse[1] = y;
|
|
|
|
|
|
|
|
|
|
if (vc->obact->sculpt && vc->obact->sculpt->pbvh) {
|
|
|
|
|
if (!ups->stroke_active) {
|
|
|
|
|
hit = sculpt_stroke_get_location(C, location, mouse);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
hit = ups->last_hit;
|
|
|
|
|
copy_v3_v3(location, ups->last_location);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hit) {
|
|
|
|
|
Brush *brush = BKE_paint_brush(paint);
|
|
|
|
|
|
|
|
|
|
*pixel_radius = project_brush_radius(
|
|
|
|
|
vc, BKE_brush_unprojected_radius_get(scene, brush), location);
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (*pixel_radius == 0) {
|
2019-04-17 06:17:24 +02:00
|
|
|
*pixel_radius = BKE_brush_size_get(scene, brush);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
mul_m4_v3(vc->obact->obmat, location);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
|
|
|
|
Brush *brush = BKE_paint_brush(&sd->paint);
|
|
|
|
|
|
|
|
|
|
*pixel_radius = BKE_brush_size_get(scene, brush);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hit;
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Draw an overlay that shows what effect the brush's texture will
|
2012-03-03 16:31:46 +00:00
|
|
|
* have on brush strength */
|
2019-04-17 06:17:24 +02:00
|
|
|
static void paint_draw_tex_overlay(UnifiedPaintSettings *ups,
|
|
|
|
|
Brush *brush,
|
|
|
|
|
ViewContext *vc,
|
|
|
|
|
int x,
|
|
|
|
|
int y,
|
|
|
|
|
float zoom,
|
|
|
|
|
bool col,
|
|
|
|
|
bool primary)
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
rctf quad;
|
|
|
|
|
/* check for overlay mode */
|
|
|
|
|
|
|
|
|
|
MTex *mtex = (primary) ? &brush->mtex : &brush->mask_mtex;
|
|
|
|
|
bool valid = ((primary) ? (brush->overlay_flags & BRUSH_OVERLAY_PRIMARY) != 0 :
|
|
|
|
|
(brush->overlay_flags & BRUSH_OVERLAY_SECONDARY) != 0);
|
|
|
|
|
int overlay_alpha = (primary) ? brush->texture_overlay_alpha : brush->mask_overlay_alpha;
|
|
|
|
|
|
|
|
|
|
if (!(mtex->tex) ||
|
|
|
|
|
!((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) ||
|
|
|
|
|
(valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (load_tex(brush, vc, zoom, col, primary)) {
|
|
|
|
|
GPU_blend(true);
|
|
|
|
|
|
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
|
glDepthFunc(GL_ALWAYS);
|
|
|
|
|
|
|
|
|
|
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
|
|
|
|
|
GPU_matrix_push();
|
|
|
|
|
|
|
|
|
|
/* brush rotation */
|
|
|
|
|
GPU_matrix_translate_2f(x, y);
|
|
|
|
|
GPU_matrix_rotate_2d(-RAD2DEGF(primary ? ups->brush_rotation : ups->brush_rotation_sec));
|
|
|
|
|
GPU_matrix_translate_2f(-x, -y);
|
|
|
|
|
|
|
|
|
|
/* scale based on tablet pressure */
|
|
|
|
|
if (primary && ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) {
|
|
|
|
|
const float scale = ups->size_pressure_value;
|
|
|
|
|
GPU_matrix_translate_2f(x, y);
|
|
|
|
|
GPU_matrix_scale_2f(scale, scale);
|
|
|
|
|
GPU_matrix_translate_2f(-x, -y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ups->draw_anchored) {
|
|
|
|
|
quad.xmin = ups->anchored_initial_mouse[0] - ups->anchored_size;
|
|
|
|
|
quad.ymin = ups->anchored_initial_mouse[1] - ups->anchored_size;
|
|
|
|
|
quad.xmax = ups->anchored_initial_mouse[0] + ups->anchored_size;
|
|
|
|
|
quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const int radius = BKE_brush_size_get(vc->scene, brush) * zoom;
|
|
|
|
|
quad.xmin = x - radius;
|
|
|
|
|
quad.ymin = y - radius;
|
|
|
|
|
quad.xmax = x + radius;
|
|
|
|
|
quad.ymax = y + radius;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
|
|
|
|
|
quad.xmin = 0;
|
|
|
|
|
quad.ymin = 0;
|
|
|
|
|
quad.xmax = BLI_rcti_size_x(&vc->ar->winrct);
|
|
|
|
|
quad.ymax = BLI_rcti_size_y(&vc->ar->winrct);
|
|
|
|
|
}
|
|
|
|
|
/* Stencil code goes here */
|
|
|
|
|
else {
|
|
|
|
|
if (primary) {
|
|
|
|
|
quad.xmin = -brush->stencil_dimension[0];
|
|
|
|
|
quad.ymin = -brush->stencil_dimension[1];
|
|
|
|
|
quad.xmax = brush->stencil_dimension[0];
|
|
|
|
|
quad.ymax = brush->stencil_dimension[1];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
quad.xmin = -brush->mask_stencil_dimension[0];
|
|
|
|
|
quad.ymin = -brush->mask_stencil_dimension[1];
|
|
|
|
|
quad.xmax = brush->mask_stencil_dimension[0];
|
|
|
|
|
quad.ymax = brush->mask_stencil_dimension[1];
|
|
|
|
|
}
|
|
|
|
|
GPU_matrix_push();
|
2019-04-22 09:19:45 +10:00
|
|
|
if (primary) {
|
2019-04-17 06:17:24 +02:00
|
|
|
GPU_matrix_translate_2fv(brush->stencil_pos);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
GPU_matrix_translate_2fv(brush->mask_stencil_pos);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
GPU_matrix_rotate_2d(RAD2DEGF(mtex->rot));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set quad color. Colored overlay does not get blending */
|
|
|
|
|
GPUVertFormat *format = immVertexFormat();
|
|
|
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
|
uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
|
|
|
|
|
|
if (col) {
|
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
|
|
|
|
|
immUniformColor4f(1.0f, 1.0f, 1.0f, overlay_alpha * 0.01f);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR);
|
|
|
|
|
immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, overlay_alpha * 0.01f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* draw textured quad */
|
|
|
|
|
immUniform1i("image", 0);
|
|
|
|
|
|
|
|
|
|
immBegin(GPU_PRIM_TRI_FAN, 4);
|
|
|
|
|
immAttr2f(texCoord, 0.0f, 0.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmin, quad.ymin);
|
|
|
|
|
immAttr2f(texCoord, 1.0f, 0.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmax, quad.ymin);
|
|
|
|
|
immAttr2f(texCoord, 1.0f, 1.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmax, quad.ymax);
|
|
|
|
|
immAttr2f(texCoord, 0.0f, 1.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmin, quad.ymax);
|
|
|
|
|
immEnd();
|
|
|
|
|
|
|
|
|
|
immUnbindProgram();
|
|
|
|
|
GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
|
|
if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_VIEW)) {
|
|
|
|
|
GPU_matrix_pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Draw an overlay that shows what effect the brush's texture will
|
|
|
|
|
* have on brush strength */
|
2018-07-02 18:45:26 +02:00
|
|
|
static void paint_draw_cursor_overlay(
|
2019-04-17 06:17:24 +02:00
|
|
|
UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, int y, float zoom)
|
2013-04-22 20:46:18 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
rctf quad;
|
|
|
|
|
/* check for overlay mode */
|
|
|
|
|
|
|
|
|
|
if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (load_tex_cursor(brush, vc, zoom)) {
|
|
|
|
|
bool do_pop = false;
|
|
|
|
|
float center[2];
|
|
|
|
|
GPU_blend(true);
|
|
|
|
|
|
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
|
glDepthFunc(GL_ALWAYS);
|
|
|
|
|
|
|
|
|
|
if (ups->draw_anchored) {
|
|
|
|
|
copy_v2_v2(center, ups->anchored_initial_mouse);
|
|
|
|
|
quad.xmin = ups->anchored_initial_mouse[0] - ups->anchored_size;
|
|
|
|
|
quad.ymin = ups->anchored_initial_mouse[1] - ups->anchored_size;
|
|
|
|
|
quad.xmax = ups->anchored_initial_mouse[0] + ups->anchored_size;
|
|
|
|
|
quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const int radius = BKE_brush_size_get(vc->scene, brush) * zoom;
|
|
|
|
|
center[0] = x;
|
|
|
|
|
center[1] = y;
|
|
|
|
|
|
|
|
|
|
quad.xmin = x - radius;
|
|
|
|
|
quad.ymin = y - radius;
|
|
|
|
|
quad.xmax = x + radius;
|
|
|
|
|
quad.ymax = y + radius;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* scale based on tablet pressure */
|
|
|
|
|
if (ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) {
|
|
|
|
|
do_pop = true;
|
|
|
|
|
GPU_matrix_push();
|
|
|
|
|
GPU_matrix_translate_2fv(center);
|
|
|
|
|
GPU_matrix_scale_1f(ups->size_pressure_value);
|
|
|
|
|
GPU_matrix_translate_2f(-center[0], -center[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GPUVertFormat *format = immVertexFormat();
|
|
|
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
|
uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
|
|
|
|
|
|
GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR);
|
|
|
|
|
|
|
|
|
|
immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, brush->cursor_overlay_alpha * 0.01f);
|
|
|
|
|
|
|
|
|
|
/* draw textured quad */
|
|
|
|
|
|
|
|
|
|
/* draw textured quad */
|
|
|
|
|
immUniform1i("image", 0);
|
|
|
|
|
|
|
|
|
|
immBegin(GPU_PRIM_TRI_FAN, 4);
|
|
|
|
|
immAttr2f(texCoord, 0.0f, 0.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmin, quad.ymin);
|
|
|
|
|
immAttr2f(texCoord, 1.0f, 0.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmax, quad.ymin);
|
|
|
|
|
immAttr2f(texCoord, 1.0f, 1.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmax, quad.ymax);
|
|
|
|
|
immAttr2f(texCoord, 0.0f, 1.0f);
|
|
|
|
|
immVertex2f(pos, quad.xmin, quad.ymax);
|
|
|
|
|
immEnd();
|
|
|
|
|
|
|
|
|
|
immUnbindProgram();
|
|
|
|
|
|
|
|
|
|
GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (do_pop) {
|
2019-04-17 06:17:24 +02:00
|
|
|
GPU_matrix_pop();
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2013-04-22 20:46:18 +00:00
|
|
|
}
|
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
|
|
|
|
|
Brush *brush,
|
|
|
|
|
ViewContext *vc,
|
|
|
|
|
int x,
|
|
|
|
|
int y,
|
|
|
|
|
float zoom,
|
|
|
|
|
ePaintMode mode)
|
2013-04-22 20:46:18 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Color means that primary brush texture is colored and
|
|
|
|
|
* secondary is used for alpha/mask control. */
|
|
|
|
|
bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX);
|
|
|
|
|
eOverlayControlFlags flags = BKE_paint_get_overlay_flags();
|
|
|
|
|
gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT);
|
|
|
|
|
|
|
|
|
|
/* Translate to region. */
|
|
|
|
|
GPU_matrix_push();
|
|
|
|
|
GPU_matrix_translate_2f(vc->ar->winrct.xmin, vc->ar->winrct.ymin);
|
|
|
|
|
x -= vc->ar->winrct.xmin;
|
|
|
|
|
y -= vc->ar->winrct.ymin;
|
|
|
|
|
|
2019-04-29 14:14:14 +10:00
|
|
|
/* Colored overlay should be drawn separately. */
|
2019-04-17 06:17:24 +02:00
|
|
|
if (col) {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GPU_matrix_pop();
|
|
|
|
|
gpuPopAttr();
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
2017-03-23 01:33:34 -03:00
|
|
|
BLI_INLINE void draw_tri_point(
|
2019-04-17 06:17:24 +02:00
|
|
|
unsigned int pos, float sel_col[4], float pivot_col[4], float *co, float width, bool selected)
|
2014-07-21 12:02:05 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
immUniformColor4fv(selected ? sel_col : pivot_col);
|
|
|
|
|
|
|
|
|
|
GPU_line_width(3.0f);
|
|
|
|
|
|
|
|
|
|
float w = width / 2.0f;
|
|
|
|
|
float tri[3][2] = {
|
|
|
|
|
{co[0], co[1] + w},
|
|
|
|
|
{co[0] - w, co[1] - w},
|
|
|
|
|
{co[0] + w, co[1] - w},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
immBegin(GPU_PRIM_LINE_LOOP, 3);
|
|
|
|
|
immVertex2fv(pos, tri[0]);
|
|
|
|
|
immVertex2fv(pos, tri[1]);
|
|
|
|
|
immVertex2fv(pos, tri[2]);
|
|
|
|
|
immEnd();
|
|
|
|
|
|
|
|
|
|
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f);
|
|
|
|
|
GPU_line_width(1.0f);
|
|
|
|
|
|
|
|
|
|
immBegin(GPU_PRIM_LINE_LOOP, 3);
|
|
|
|
|
immVertex2fv(pos, tri[0]);
|
|
|
|
|
immVertex2fv(pos, tri[1]);
|
|
|
|
|
immVertex2fv(pos, tri[2]);
|
|
|
|
|
immEnd();
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-23 01:33:34 -03:00
|
|
|
BLI_INLINE void draw_rect_point(
|
2019-04-17 06:17:24 +02:00
|
|
|
unsigned int pos, float sel_col[4], float handle_col[4], float *co, float width, bool selected)
|
2014-07-21 12:02:05 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
immUniformColor4fv(selected ? sel_col : handle_col);
|
2017-04-16 12:25:42 -04:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
GPU_line_width(3.0f);
|
2017-03-23 01:33:34 -03:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
float w = width / 2.0f;
|
|
|
|
|
float minx = co[0] - w;
|
|
|
|
|
float miny = co[1] - w;
|
|
|
|
|
float maxx = co[0] + w;
|
|
|
|
|
float maxy = co[1] + w;
|
2017-03-23 01:33:34 -03:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy);
|
2017-03-23 01:33:34 -03:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f);
|
|
|
|
|
GPU_line_width(1.0f);
|
2017-03-23 01:33:34 -03:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy);
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-23 01:33:34 -03:00
|
|
|
BLI_INLINE void draw_bezier_handle_lines(unsigned int pos, float sel_col[4], BezTriple *bez)
|
2014-07-21 12:02:05 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
|
|
|
|
GPU_line_width(3.0f);
|
|
|
|
|
|
|
|
|
|
immBegin(GPU_PRIM_LINE_STRIP, 3);
|
|
|
|
|
immVertex2fv(pos, bez->vec[0]);
|
|
|
|
|
immVertex2fv(pos, bez->vec[1]);
|
|
|
|
|
immVertex2fv(pos, bez->vec[2]);
|
|
|
|
|
immEnd();
|
|
|
|
|
|
|
|
|
|
GPU_line_width(1.0f);
|
|
|
|
|
|
|
|
|
|
if (bez->f1 || bez->f2) {
|
|
|
|
|
immUniformColor4fv(sel_col);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f);
|
|
|
|
|
}
|
|
|
|
|
immBegin(GPU_PRIM_LINES, 2);
|
|
|
|
|
immVertex2fv(pos, bez->vec[0]);
|
|
|
|
|
immVertex2fv(pos, bez->vec[1]);
|
|
|
|
|
immEnd();
|
|
|
|
|
|
|
|
|
|
if (bez->f3 || bez->f2) {
|
|
|
|
|
immUniformColor4fv(sel_col);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f);
|
|
|
|
|
}
|
|
|
|
|
immBegin(GPU_PRIM_LINES, 2);
|
|
|
|
|
immVertex2fv(pos, bez->vec[1]);
|
|
|
|
|
immVertex2fv(pos, bez->vec[2]);
|
|
|
|
|
immEnd();
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-16 21:36:22 -03:00
|
|
|
static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc)
|
2014-07-21 12:02:05 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
GPU_matrix_push();
|
|
|
|
|
GPU_matrix_translate_2f(vc->ar->winrct.xmin, vc->ar->winrct.ymin);
|
|
|
|
|
|
|
|
|
|
if (brush->paint_curve && brush->paint_curve->points) {
|
|
|
|
|
int i;
|
|
|
|
|
PaintCurve *pc = brush->paint_curve;
|
|
|
|
|
PaintCurvePoint *cp = pc->points;
|
|
|
|
|
|
|
|
|
|
GPU_line_smooth(true);
|
|
|
|
|
GPU_blend(true);
|
|
|
|
|
|
|
|
|
|
/* draw the bezier handles and the curve segment between the current and next point */
|
|
|
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
|
|
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
|
|
|
|
|
|
|
|
float selec_col[4], handle_col[4], pivot_col[4];
|
2019-07-16 20:31:23 +02:00
|
|
|
UI_GetThemeColorType4fv(TH_VERTEX_SELECT, SPACE_VIEW3D, selec_col);
|
|
|
|
|
UI_GetThemeColorType4fv(TH_PAINT_CURVE_HANDLE, SPACE_VIEW3D, handle_col);
|
|
|
|
|
UI_GetThemeColorType4fv(TH_PAINT_CURVE_PIVOT, SPACE_VIEW3D, pivot_col);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
for (i = 0; i < pc->tot_points - 1; i++, cp++) {
|
|
|
|
|
int j;
|
|
|
|
|
PaintCurvePoint *cp_next = cp + 1;
|
|
|
|
|
float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2];
|
|
|
|
|
/* use color coding to distinguish handles vs curve segments */
|
|
|
|
|
draw_bezier_handle_lines(pos, selec_col, &cp->bez);
|
|
|
|
|
draw_tri_point(pos, selec_col, pivot_col, &cp->bez.vec[1][0], 10.0f, cp->bez.f2);
|
|
|
|
|
draw_rect_point(
|
|
|
|
|
pos, selec_col, handle_col, &cp->bez.vec[0][0], 8.0f, cp->bez.f1 || cp->bez.f2);
|
|
|
|
|
draw_rect_point(
|
|
|
|
|
pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2);
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
for (j = 0; j < 2; j++) {
|
2019-04-17 06:17:24 +02:00
|
|
|
BKE_curve_forward_diff_bezier(cp->bez.vec[1][j],
|
|
|
|
|
cp->bez.vec[2][j],
|
|
|
|
|
cp_next->bez.vec[0][j],
|
|
|
|
|
cp_next->bez.vec[1][j],
|
|
|
|
|
data + j,
|
|
|
|
|
PAINT_CURVE_NUM_SEGMENTS,
|
|
|
|
|
sizeof(float[2]));
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
float(*v)[2] = (float(*)[2])data;
|
|
|
|
|
|
|
|
|
|
immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f);
|
|
|
|
|
GPU_line_width(3.0f);
|
|
|
|
|
immBegin(GPU_PRIM_LINE_STRIP, PAINT_CURVE_NUM_SEGMENTS + 1);
|
|
|
|
|
for (j = 0; j <= PAINT_CURVE_NUM_SEGMENTS; j++) {
|
|
|
|
|
immVertex2fv(pos, v[j]);
|
|
|
|
|
}
|
|
|
|
|
immEnd();
|
|
|
|
|
|
|
|
|
|
immUniformColor4f(0.9f, 0.9f, 1.0f, 0.5f);
|
|
|
|
|
GPU_line_width(1.0f);
|
|
|
|
|
immBegin(GPU_PRIM_LINE_STRIP, PAINT_CURVE_NUM_SEGMENTS + 1);
|
|
|
|
|
for (j = 0; j <= PAINT_CURVE_NUM_SEGMENTS; j++) {
|
|
|
|
|
immVertex2fv(pos, v[j]);
|
|
|
|
|
}
|
|
|
|
|
immEnd();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* draw last line segment */
|
|
|
|
|
draw_bezier_handle_lines(pos, selec_col, &cp->bez);
|
|
|
|
|
draw_tri_point(pos, selec_col, pivot_col, &cp->bez.vec[1][0], 10.0f, cp->bez.f2);
|
|
|
|
|
draw_rect_point(
|
|
|
|
|
pos, selec_col, handle_col, &cp->bez.vec[0][0], 8.0f, cp->bez.f1 || cp->bez.f2);
|
|
|
|
|
draw_rect_point(
|
|
|
|
|
pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2);
|
|
|
|
|
|
|
|
|
|
GPU_blend(false);
|
|
|
|
|
GPU_line_smooth(false);
|
|
|
|
|
|
|
|
|
|
immUnbindProgram();
|
|
|
|
|
}
|
|
|
|
|
GPU_matrix_pop();
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
|
2012-01-19 02:06:09 +00:00
|
|
|
/* Special actions taken when paint cursor goes over mesh */
|
|
|
|
|
/* TODO: sculpt only for now */
|
2019-04-17 06:17:24 +02:00
|
|
|
static void paint_cursor_on_hit(UnifiedPaintSettings *ups,
|
|
|
|
|
Brush *brush,
|
|
|
|
|
ViewContext *vc,
|
|
|
|
|
const float location[3])
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
float unprojected_radius, projected_radius;
|
|
|
|
|
|
|
|
|
|
/* update the brush's cached 3D radius */
|
|
|
|
|
if (!BKE_brush_use_locked_size(vc->scene, brush)) {
|
|
|
|
|
/* get 2D brush radius */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ups->draw_anchored) {
|
2019-04-17 06:17:24 +02:00
|
|
|
projected_radius = ups->anchored_size;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
else {
|
2019-04-22 09:19:45 +10:00
|
|
|
if (brush->flag & BRUSH_ANCHORED) {
|
2019-04-17 06:17:24 +02:00
|
|
|
projected_radius = 8;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2019-04-17 06:17:24 +02:00
|
|
|
projected_radius = BKE_brush_size_get(vc->scene, brush);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* convert brush radius from 2D to 3D */
|
|
|
|
|
unprojected_radius = paint_calc_object_space_radius(vc, location, projected_radius);
|
|
|
|
|
|
|
|
|
|
/* scale 3D brush radius by pressure */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
unprojected_radius *= ups->size_pressure_value;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
/* set cached value in either Brush or UnifiedPaintSettings */
|
|
|
|
|
BKE_brush_unprojected_radius_set(vc->scene, brush, unprojected_radius);
|
|
|
|
|
}
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
2017-10-17 13:43:10 +11:00
|
|
|
static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush)
|
2015-06-03 12:04:47 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (paint->flags & PAINT_SHOW_BRUSH) {
|
|
|
|
|
if (ELEM(mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D) &&
|
|
|
|
|
brush->imagepaint_tool == PAINT_TOOL_FILL) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2015-06-03 12:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
2012-01-19 02:06:09 +00:00
|
|
|
static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
|
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
ARegion *ar = CTX_wm_region(C);
|
|
|
|
|
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
|
|
|
|
|
Paint *paint = BKE_paint_get_active_from_context(C);
|
|
|
|
|
Brush *brush = BKE_paint_brush(paint);
|
|
|
|
|
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
|
|
|
|
|
|
|
|
|
|
/* check that brush drawing is enabled */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (ommit_cursor_drawing(paint, mode, brush)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return;
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
/* can't use stroke vc here because this will be called during
|
|
|
|
|
* mouse over too, not just during a stroke */
|
|
|
|
|
ViewContext vc;
|
|
|
|
|
ED_view3d_viewcontext_init(C, &vc);
|
|
|
|
|
|
|
|
|
|
if (vc.rv3d && (vc.rv3d->rflag & RV3D_NAVIGATING)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* skip everything and draw brush here */
|
|
|
|
|
if (brush->flag & BRUSH_CURVE) {
|
|
|
|
|
paint_draw_curve_cursor(brush, &vc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float zoomx, zoomy;
|
|
|
|
|
get_imapaint_zoom(C, &zoomx, &zoomy);
|
|
|
|
|
zoomx = max_ff(zoomx, zoomy);
|
|
|
|
|
|
|
|
|
|
/* set various defaults */
|
|
|
|
|
const float *outline_col = brush->add_col;
|
|
|
|
|
const float outline_alpha = 0.5f;
|
|
|
|
|
float translation[2] = {x, y};
|
|
|
|
|
float final_radius = (BKE_brush_size_get(scene, brush) * zoomx);
|
|
|
|
|
|
|
|
|
|
/* don't calculate rake angles while a stroke is active because the rake variables are global
|
|
|
|
|
* and we may get interference with the stroke itself.
|
|
|
|
|
* For line strokes, such interference is visible */
|
|
|
|
|
if (!ups->stroke_active) {
|
|
|
|
|
paint_calculate_rake_rotation(ups, brush, translation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* draw overlay */
|
|
|
|
|
paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode);
|
|
|
|
|
|
|
|
|
|
/* TODO: as sculpt and other paint modes are unified, this
|
|
|
|
|
* special mode of drawing will go away */
|
|
|
|
|
if ((mode == PAINT_MODE_SCULPT) && vc.obact->sculpt) {
|
|
|
|
|
float location[3];
|
|
|
|
|
int pixel_radius;
|
|
|
|
|
|
|
|
|
|
/* test if brush is over the mesh */
|
|
|
|
|
bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups);
|
|
|
|
|
|
2019-04-22 09:19:45 +10:00
|
|
|
if (BKE_brush_use_locked_size(scene, brush)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
BKE_brush_size_set(scene, brush, pixel_radius);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
|
/* check if brush is subtracting, use different color then */
|
|
|
|
|
/* TODO: no way currently to know state of pen flip or
|
|
|
|
|
* invert key modifier without starting a stroke */
|
|
|
|
|
if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) &&
|
|
|
|
|
BKE_brush_sculpt_has_secondary_color(brush)) {
|
|
|
|
|
outline_col = brush->sub_col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* only do if brush is over the mesh */
|
2019-04-22 09:19:45 +10:00
|
|
|
if (hit) {
|
2019-04-17 06:17:24 +02:00
|
|
|
paint_cursor_on_hit(ups, brush, &vc, location);
|
2019-04-22 09:19:45 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ups->draw_anchored) {
|
|
|
|
|
final_radius = ups->anchored_size;
|
|
|
|
|
copy_v2_fl2(translation,
|
|
|
|
|
ups->anchored_initial_mouse[0] + ar->winrct.xmin,
|
|
|
|
|
ups->anchored_initial_mouse[1] + ar->winrct.ymin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* make lines pretty */
|
|
|
|
|
GPU_line_width(1.0f);
|
|
|
|
|
GPU_blend(true); /* TODO: also set blend mode? */
|
|
|
|
|
GPU_line_smooth(true);
|
|
|
|
|
|
|
|
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
|
|
|
|
|
|
|
|
/* set brush color */
|
|
|
|
|
immUniformColor3fvAlpha(outline_col, outline_alpha);
|
|
|
|
|
|
|
|
|
|
/* draw brush outline */
|
|
|
|
|
if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) {
|
|
|
|
|
/* inner at full alpha */
|
|
|
|
|
imm_draw_circle_wire_2d(
|
|
|
|
|
pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40);
|
|
|
|
|
/* outer at half alpha */
|
|
|
|
|
immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f);
|
|
|
|
|
}
|
|
|
|
|
imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40);
|
|
|
|
|
|
|
|
|
|
immUnbindProgram();
|
|
|
|
|
|
|
|
|
|
/* restore GL state */
|
|
|
|
|
GPU_blend(false);
|
|
|
|
|
GPU_line_smooth(false);
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Public API */
|
|
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
void paint_cursor_start(bContext *C, bool (*poll)(bContext *C))
|
2012-01-19 02:06:09 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Paint *p = BKE_paint_get_active_from_context(C);
|
|
|
|
|
|
|
|
|
|
if (p && !p->paint_cursor) {
|
|
|
|
|
p->paint_cursor = WM_paint_cursor_activate(
|
|
|
|
|
CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, poll, paint_draw_cursor, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* invalidate the paint cursors */
|
|
|
|
|
BKE_paint_invalidate_overlay_all();
|
2012-01-19 02:06:09 +00:00
|
|
|
}
|
2013-03-29 14:02:28 +00:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
void paint_cursor_start_explicit(Paint *p, wmWindowManager *wm, bool (*poll)(bContext *C))
|
2013-03-29 14:02:28 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (p && !p->paint_cursor) {
|
|
|
|
|
p->paint_cursor = WM_paint_cursor_activate(
|
|
|
|
|
wm, SPACE_TYPE_ANY, RGN_TYPE_ANY, poll, paint_draw_cursor, NULL);
|
|
|
|
|
}
|
2013-03-29 14:02:28 +00:00
|
|
|
}
|