|
|
|
|
@@ -35,7 +35,7 @@
|
|
|
|
|
#include "BLI_math.h"
|
|
|
|
|
#include "BLI_array_utils.h"
|
|
|
|
|
#include "BLI_bitmap.h"
|
|
|
|
|
#include "BLI_stack.h"
|
|
|
|
|
#include "BLI_task.h"
|
|
|
|
|
#include "BLI_string_utils.h"
|
|
|
|
|
|
|
|
|
|
#include "IMB_imbuf.h"
|
|
|
|
|
@@ -66,6 +66,7 @@
|
|
|
|
|
#include "BKE_paint.h"
|
|
|
|
|
#include "BKE_report.h"
|
|
|
|
|
#include "BKE_colortools.h"
|
|
|
|
|
#include "BKE_subsurf.h"
|
|
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
@@ -76,14 +77,30 @@
|
|
|
|
|
#include "ED_screen.h"
|
|
|
|
|
#include "ED_view3d.h"
|
|
|
|
|
|
|
|
|
|
#include "bmesh.h"
|
|
|
|
|
#include "BKE_ccg.h"
|
|
|
|
|
|
|
|
|
|
#include "sculpt_intern.h"
|
|
|
|
|
#include "paint_intern.h" /* own include */
|
|
|
|
|
|
|
|
|
|
/* small structure to defer applying weight-paint results */
|
|
|
|
|
struct WPaintDefer {
|
|
|
|
|
int index;
|
|
|
|
|
float alpha, weight;
|
|
|
|
|
/* Use for 'blur' brush, align with PBVH nodes, created and freed on each update. */
|
|
|
|
|
struct VPaintAverageAccum {
|
|
|
|
|
uint len;
|
|
|
|
|
uint value[3];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct WPaintAverageAccum {
|
|
|
|
|
uint len;
|
|
|
|
|
double value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void defweight_prev_init(const MDeformWeight *dw, float *weight_prev)
|
|
|
|
|
{
|
|
|
|
|
if (UNLIKELY(*weight_prev == -1.0f)) {
|
|
|
|
|
*weight_prev = dw ? dw->weight : 0.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check if we can do partial updates and have them draw realtime
|
|
|
|
|
* (without rebuilding the 'derivedFinal') */
|
|
|
|
|
static bool vertex_paint_use_fast_update_check(Object *ob)
|
|
|
|
|
@@ -174,24 +191,19 @@ static VPaint *new_vpaint(int wpaint)
|
|
|
|
|
return vp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int *get_indexarray(Mesh *me)
|
|
|
|
|
{
|
|
|
|
|
return MEM_mallocN(sizeof(int) * (me->totpoly + 1), "vertexpaint");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int vpaint_get_current_col(Scene *scene, VPaint *vp)
|
|
|
|
|
uint vpaint_get_current_col(Scene *scene, VPaint *vp)
|
|
|
|
|
{
|
|
|
|
|
Brush *brush = BKE_paint_brush(&vp->paint);
|
|
|
|
|
unsigned char col[4];
|
|
|
|
|
uchar col[4];
|
|
|
|
|
rgb_float_to_uchar(col, BKE_brush_color_get(scene, brush));
|
|
|
|
|
col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */
|
|
|
|
|
return *(unsigned int *)col;
|
|
|
|
|
return *(uint *)col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_shared_vertexcol(Mesh *me, bool *mlooptag)
|
|
|
|
|
{
|
|
|
|
|
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
MPoly *mp;
|
|
|
|
|
const MPoly *mp;
|
|
|
|
|
int (*scol)[4];
|
|
|
|
|
int i, j;
|
|
|
|
|
bool has_shared = false;
|
|
|
|
|
@@ -205,7 +217,7 @@ static void do_shared_vertexcol(Mesh *me, bool *mlooptag)
|
|
|
|
|
|
|
|
|
|
for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
|
|
|
|
|
if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
|
|
|
|
|
MLoop *ml = me->mloop + mp->loopstart;
|
|
|
|
|
const MLoop *ml = me->mloop + mp->loopstart;
|
|
|
|
|
MLoopCol *lcol = me->mloopcol + mp->loopstart;
|
|
|
|
|
for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
|
|
|
|
|
scol[ml->v][0] += lcol->r;
|
|
|
|
|
@@ -228,7 +240,7 @@ static void do_shared_vertexcol(Mesh *me, bool *mlooptag)
|
|
|
|
|
|
|
|
|
|
for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
|
|
|
|
|
if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
|
|
|
|
|
MLoop *ml = me->mloop + mp->loopstart;
|
|
|
|
|
const MLoop *ml = me->mloop + mp->loopstart;
|
|
|
|
|
MLoopCol *lcol = me->mloopcol + mp->loopstart;
|
|
|
|
|
for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
|
|
|
|
|
if (mlooptag[mp->loopstart + j]) {
|
|
|
|
|
@@ -292,55 +304,42 @@ static int wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void free_vpaint_prev(VPaint *vp)
|
|
|
|
|
struct WPaintPrev {
|
|
|
|
|
struct MDeformVert *wpaint_prev; /* previous vertex weights */
|
|
|
|
|
int tot; /* allocation size of prev buffers */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void wpaint_prev_init(struct WPaintPrev *wpp)
|
|
|
|
|
{
|
|
|
|
|
if (vp->vpaint_prev) {
|
|
|
|
|
MEM_freeN(vp->vpaint_prev);
|
|
|
|
|
vp->vpaint_prev = NULL;
|
|
|
|
|
vp->tot = 0;
|
|
|
|
|
}
|
|
|
|
|
wpp->wpaint_prev = NULL;
|
|
|
|
|
wpp->tot = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void free_wpaint_prev(VPaint *vp)
|
|
|
|
|
static void wpaint_prev_create(struct WPaintPrev *wpp, MDeformVert *dverts, int dcount)
|
|
|
|
|
{
|
|
|
|
|
if (vp->wpaint_prev) {
|
|
|
|
|
BKE_defvert_array_free(vp->wpaint_prev, vp->tot);
|
|
|
|
|
vp->wpaint_prev = NULL;
|
|
|
|
|
vp->tot = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
wpaint_prev_init(wpp);
|
|
|
|
|
|
|
|
|
|
static void copy_vpaint_prev(VPaint *vp, unsigned int *lcol, int tot)
|
|
|
|
|
{
|
|
|
|
|
free_vpaint_prev(vp);
|
|
|
|
|
|
|
|
|
|
vp->tot = tot;
|
|
|
|
|
|
|
|
|
|
if (lcol == NULL || tot == 0) return;
|
|
|
|
|
|
|
|
|
|
vp->vpaint_prev = MEM_mallocN(sizeof(int) * tot, "vpaint_prev");
|
|
|
|
|
memcpy(vp->vpaint_prev, lcol, sizeof(int) * tot);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void copy_wpaint_prev(VPaint *wp, MDeformVert *dverts, int dcount)
|
|
|
|
|
{
|
|
|
|
|
free_wpaint_prev(wp);
|
|
|
|
|
|
|
|
|
|
if (dverts && dcount) {
|
|
|
|
|
|
|
|
|
|
wp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
|
|
|
|
|
wp->tot = dcount;
|
|
|
|
|
BKE_defvert_array_copy(wp->wpaint_prev, dverts, dcount);
|
|
|
|
|
wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
|
|
|
|
|
wpp->tot = dcount;
|
|
|
|
|
BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ED_vpaint_fill(Object *ob, unsigned int paintcol)
|
|
|
|
|
static void wpaint_prev_destroy(struct WPaintPrev *wpp)
|
|
|
|
|
{
|
|
|
|
|
if (wpp->wpaint_prev) {
|
|
|
|
|
BKE_defvert_array_free(wpp->wpaint_prev, wpp->tot);
|
|
|
|
|
}
|
|
|
|
|
wpp->wpaint_prev = NULL;
|
|
|
|
|
wpp->tot = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ED_vpaint_fill(Object *ob, uint paintcol)
|
|
|
|
|
{
|
|
|
|
|
Mesh *me;
|
|
|
|
|
MPoly *mp;
|
|
|
|
|
const MPoly *mp;
|
|
|
|
|
int i, j;
|
|
|
|
|
bool selected;
|
|
|
|
|
|
|
|
|
|
if (((me = BKE_mesh_from_object(ob)) == NULL) ||
|
|
|
|
|
(me->mloopcol == NULL && (make_vertexcol(ob) == false)))
|
|
|
|
|
@@ -348,13 +347,13 @@ bool ED_vpaint_fill(Object *ob, unsigned int paintcol)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selected = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
mp = me->mpoly;
|
|
|
|
|
for (i = 0; i < me->totpoly; i++, mp++) {
|
|
|
|
|
MLoopCol *lcol = me->mloopcol + mp->loopstart;
|
|
|
|
|
|
|
|
|
|
if (selected && !(mp->flag & ME_FACE_SEL))
|
|
|
|
|
if (use_face_sel && !(mp->flag & ME_FACE_SEL))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < mp->totloop; j++, lcol++) {
|
|
|
|
|
@@ -372,13 +371,13 @@ bool ED_vpaint_fill(Object *ob, unsigned int paintcol)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* fills in the selected faces with the current weight and vertex group */
|
|
|
|
|
bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
|
|
|
|
|
bool ED_wpaint_fill(Object *ob, float paintweight)
|
|
|
|
|
{
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
MPoly *mp;
|
|
|
|
|
const MPoly *mp;
|
|
|
|
|
MDeformWeight *dw, *dw_prev;
|
|
|
|
|
int vgroup_active, vgroup_mirror = -1;
|
|
|
|
|
unsigned int index;
|
|
|
|
|
uint index;
|
|
|
|
|
const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
|
|
|
|
|
|
|
|
|
/* mutually exclusive, could be made into a */
|
|
|
|
|
@@ -395,17 +394,18 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
|
|
|
|
|
vgroup_mirror = wpaint_mirror_vgroup_ensure(ob, vgroup_active);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy_wpaint_prev(wp, me->dvert, me->totvert);
|
|
|
|
|
struct WPaintPrev wpp;
|
|
|
|
|
wpaint_prev_create(&wpp, me->dvert, me->totvert);
|
|
|
|
|
|
|
|
|
|
for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
|
|
|
|
|
unsigned int fidx = mp->totloop - 1;
|
|
|
|
|
uint fidx = mp->totloop - 1;
|
|
|
|
|
|
|
|
|
|
if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
unsigned int vidx = me->mloop[mp->loopstart + fidx].v;
|
|
|
|
|
uint vidx = me->mloop[mp->loopstart + fidx].v;
|
|
|
|
|
|
|
|
|
|
if (!me->dvert[vidx].flag) {
|
|
|
|
|
if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
|
|
|
|
|
@@ -414,7 +414,7 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
|
|
|
|
|
|
|
|
|
|
dw = defvert_verify_index(&me->dvert[vidx], vgroup_active);
|
|
|
|
|
if (dw) {
|
|
|
|
|
dw_prev = defvert_verify_index(wp->wpaint_prev + vidx, vgroup_active);
|
|
|
|
|
dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active);
|
|
|
|
|
dw_prev->weight = dw->weight; /* set the undo weight */
|
|
|
|
|
dw->weight = paintweight;
|
|
|
|
|
|
|
|
|
|
@@ -424,11 +424,11 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
|
|
|
|
|
/* copy, not paint again */
|
|
|
|
|
if (vgroup_mirror != -1) {
|
|
|
|
|
dw = defvert_verify_index(me->dvert + j, vgroup_mirror);
|
|
|
|
|
dw_prev = defvert_verify_index(wp->wpaint_prev + j, vgroup_mirror);
|
|
|
|
|
dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_mirror);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dw = defvert_verify_index(me->dvert + j, vgroup_active);
|
|
|
|
|
dw_prev = defvert_verify_index(wp->wpaint_prev + j, vgroup_active);
|
|
|
|
|
dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_active);
|
|
|
|
|
}
|
|
|
|
|
dw_prev->weight = dw->weight; /* set the undo weight */
|
|
|
|
|
dw->weight = paintweight;
|
|
|
|
|
@@ -448,7 +448,7 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy_wpaint_prev(wp, NULL, 0);
|
|
|
|
|
wpaint_prev_destroy(&wpp);
|
|
|
|
|
|
|
|
|
|
DAG_id_tag_update(&me->id, 0);
|
|
|
|
|
|
|
|
|
|
@@ -458,12 +458,11 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
|
|
|
|
|
bool ED_vpaint_smooth(Object *ob)
|
|
|
|
|
{
|
|
|
|
|
Mesh *me;
|
|
|
|
|
MPoly *mp;
|
|
|
|
|
const MPoly *mp;
|
|
|
|
|
|
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
|
|
bool *mlooptag;
|
|
|
|
|
bool selected;
|
|
|
|
|
|
|
|
|
|
if (((me = BKE_mesh_from_object(ob)) == NULL) ||
|
|
|
|
|
(me->mloopcol == NULL && (make_vertexcol(ob) == false)))
|
|
|
|
|
@@ -471,17 +470,17 @@ bool ED_vpaint_smooth(Object *ob)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selected = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
|
|
|
|
|
|
|
|
|
|
/* simply tag loops of selected faces */
|
|
|
|
|
mp = me->mpoly;
|
|
|
|
|
for (i = 0; i < me->totpoly; i++, mp++) {
|
|
|
|
|
MLoop *ml = me->mloop + mp->loopstart;
|
|
|
|
|
const MLoop *ml = me->mloop + mp->loopstart;
|
|
|
|
|
int ml_index = mp->loopstart;
|
|
|
|
|
|
|
|
|
|
if (selected && !(mp->flag & ME_FACE_SEL))
|
|
|
|
|
if (use_face_sel && !(mp->flag & ME_FACE_SEL))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < mp->totloop; j++, ml_index++, ml++) {
|
|
|
|
|
@@ -518,13 +517,13 @@ bool ED_vpaint_color_transform(
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool do_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
mp = me->mpoly;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < me->totpoly; i++, mp++) {
|
|
|
|
|
MLoopCol *lcol = &me->mloopcol[mp->loopstart];
|
|
|
|
|
|
|
|
|
|
if (do_face_sel && !(mp->flag & ME_FACE_SEL)) {
|
|
|
|
|
if (use_face_sel && !(mp->flag & ME_FACE_SEL)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -555,7 +554,7 @@ void vpaint_dogamma(Scene *scene)
|
|
|
|
|
Object *ob;
|
|
|
|
|
float igam, fac;
|
|
|
|
|
int a, temp;
|
|
|
|
|
unsigned char *cp, gamtab[256];
|
|
|
|
|
uchar *cp, gamtab[256];
|
|
|
|
|
|
|
|
|
|
ob = OBACT;
|
|
|
|
|
me = BKE_mesh_from_object(ob);
|
|
|
|
|
@@ -577,7 +576,7 @@ void vpaint_dogamma(Scene *scene)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = 4 * me->totface;
|
|
|
|
|
cp = (unsigned char *)me->mcol;
|
|
|
|
|
cp = (uchar *)me->mcol;
|
|
|
|
|
while (a--) {
|
|
|
|
|
|
|
|
|
|
cp[1] = gamtab[cp[1]];
|
|
|
|
|
@@ -589,11 +588,11 @@ void vpaint_dogamma(Scene *scene)
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
BLI_INLINE unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
BLI_INLINE uint mcol_blend(uint col1, uint col2, int fac)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *cp1, *cp2, *cp;
|
|
|
|
|
uchar *cp1, *cp2, *cp;
|
|
|
|
|
int mfac;
|
|
|
|
|
unsigned int col = 0;
|
|
|
|
|
uint col = 0;
|
|
|
|
|
|
|
|
|
|
if (fac == 0) {
|
|
|
|
|
return col1;
|
|
|
|
|
@@ -605,31 +604,40 @@ BLI_INLINE unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac
|
|
|
|
|
|
|
|
|
|
mfac = 255 - fac;
|
|
|
|
|
|
|
|
|
|
cp1 = (unsigned char *)&col1;
|
|
|
|
|
cp2 = (unsigned char *)&col2;
|
|
|
|
|
cp = (unsigned char *)&col;
|
|
|
|
|
cp1 = (uchar *)&col1;
|
|
|
|
|
cp2 = (uchar *)&col2;
|
|
|
|
|
cp = (uchar *)&col;
|
|
|
|
|
|
|
|
|
|
cp[0] = divide_round_i((mfac * cp1[0] + fac * cp2[0]), 255);
|
|
|
|
|
cp[1] = divide_round_i((mfac * cp1[1] + fac * cp2[1]), 255);
|
|
|
|
|
cp[2] = divide_round_i((mfac * cp1[2] + fac * cp2[2]), 255);
|
|
|
|
|
/* Updated to use the rgb squared color model which blends nicer. */
|
|
|
|
|
int r1 = cp1[0] * cp1[0];
|
|
|
|
|
int g1 = cp1[1] * cp1[1];
|
|
|
|
|
int b1 = cp1[2] * cp1[2];
|
|
|
|
|
|
|
|
|
|
int r2 = cp2[0] * cp2[0];
|
|
|
|
|
int g2 = cp2[1] * cp2[1];
|
|
|
|
|
int b2 = cp2[2] * cp2[2];
|
|
|
|
|
|
|
|
|
|
cp[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255)));
|
|
|
|
|
cp[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255)));
|
|
|
|
|
cp[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255)));
|
|
|
|
|
cp[3] = 255;
|
|
|
|
|
|
|
|
|
|
return col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_INLINE unsigned int mcol_add(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
BLI_INLINE uint mcol_add(uint col1, uint col2, int fac)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *cp1, *cp2, *cp;
|
|
|
|
|
uchar *cp1, *cp2, *cp;
|
|
|
|
|
int temp;
|
|
|
|
|
unsigned int col = 0;
|
|
|
|
|
uint col = 0;
|
|
|
|
|
|
|
|
|
|
if (fac == 0) {
|
|
|
|
|
return col1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cp1 = (unsigned char *)&col1;
|
|
|
|
|
cp2 = (unsigned char *)&col2;
|
|
|
|
|
cp = (unsigned char *)&col;
|
|
|
|
|
cp1 = (uchar *)&col1;
|
|
|
|
|
cp2 = (uchar *)&col2;
|
|
|
|
|
cp = (uchar *)&col;
|
|
|
|
|
|
|
|
|
|
temp = cp1[0] + divide_round_i((fac * cp2[0]), 255);
|
|
|
|
|
cp[0] = (temp > 254) ? 255 : temp;
|
|
|
|
|
@@ -642,19 +650,19 @@ BLI_INLINE unsigned int mcol_add(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
return col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_INLINE unsigned int mcol_sub(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
BLI_INLINE uint mcol_sub(uint col1, uint col2, int fac)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *cp1, *cp2, *cp;
|
|
|
|
|
uchar *cp1, *cp2, *cp;
|
|
|
|
|
int temp;
|
|
|
|
|
unsigned int col = 0;
|
|
|
|
|
uint col = 0;
|
|
|
|
|
|
|
|
|
|
if (fac == 0) {
|
|
|
|
|
return col1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cp1 = (unsigned char *)&col1;
|
|
|
|
|
cp2 = (unsigned char *)&col2;
|
|
|
|
|
cp = (unsigned char *)&col;
|
|
|
|
|
cp1 = (uchar *)&col1;
|
|
|
|
|
cp2 = (uchar *)&col2;
|
|
|
|
|
cp = (uchar *)&col;
|
|
|
|
|
|
|
|
|
|
temp = cp1[0] - divide_round_i((fac * cp2[0]), 255);
|
|
|
|
|
cp[0] = (temp < 0) ? 0 : temp;
|
|
|
|
|
@@ -667,11 +675,11 @@ BLI_INLINE unsigned int mcol_sub(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
return col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_INLINE unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
BLI_INLINE uint mcol_mul(uint col1, uint col2, int fac)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *cp1, *cp2, *cp;
|
|
|
|
|
uchar *cp1, *cp2, *cp;
|
|
|
|
|
int mfac;
|
|
|
|
|
unsigned int col = 0;
|
|
|
|
|
uint col = 0;
|
|
|
|
|
|
|
|
|
|
if (fac == 0) {
|
|
|
|
|
return col1;
|
|
|
|
|
@@ -679,9 +687,9 @@ BLI_INLINE unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
|
|
|
|
|
mfac = 255 - fac;
|
|
|
|
|
|
|
|
|
|
cp1 = (unsigned char *)&col1;
|
|
|
|
|
cp2 = (unsigned char *)&col2;
|
|
|
|
|
cp = (unsigned char *)&col;
|
|
|
|
|
cp1 = (uchar *)&col1;
|
|
|
|
|
cp2 = (uchar *)&col2;
|
|
|
|
|
cp = (uchar *)&col;
|
|
|
|
|
|
|
|
|
|
/* first mul, then blend the fac */
|
|
|
|
|
cp[0] = divide_round_i(mfac * cp1[0] * 255 + fac * cp2[0] * cp1[0], 255 * 255);
|
|
|
|
|
@@ -692,11 +700,11 @@ BLI_INLINE unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
return col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
BLI_INLINE uint mcol_lighten(uint col1, uint col2, int fac)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *cp1, *cp2, *cp;
|
|
|
|
|
uchar *cp1, *cp2, *cp;
|
|
|
|
|
int mfac;
|
|
|
|
|
unsigned int col = 0;
|
|
|
|
|
uint col = 0;
|
|
|
|
|
|
|
|
|
|
if (fac == 0) {
|
|
|
|
|
return col1;
|
|
|
|
|
@@ -707,9 +715,9 @@ BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int f
|
|
|
|
|
|
|
|
|
|
mfac = 255 - fac;
|
|
|
|
|
|
|
|
|
|
cp1 = (unsigned char *)&col1;
|
|
|
|
|
cp2 = (unsigned char *)&col2;
|
|
|
|
|
cp = (unsigned char *)&col;
|
|
|
|
|
cp1 = (uchar *)&col1;
|
|
|
|
|
cp2 = (uchar *)&col2;
|
|
|
|
|
cp = (uchar *)&col;
|
|
|
|
|
|
|
|
|
|
/* See if are lighter, if so mix, else don't do anything.
|
|
|
|
|
* if the paint col is darker then the original, then ignore */
|
|
|
|
|
@@ -725,11 +733,11 @@ BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int f
|
|
|
|
|
return col;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fac)
|
|
|
|
|
BLI_INLINE uint mcol_darken(uint col1, uint col2, int fac)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *cp1, *cp2, *cp;
|
|
|
|
|
uchar *cp1, *cp2, *cp;
|
|
|
|
|
int mfac;
|
|
|
|
|
unsigned int col = 0;
|
|
|
|
|
uint col = 0;
|
|
|
|
|
|
|
|
|
|
if (fac == 0) {
|
|
|
|
|
return col1;
|
|
|
|
|
@@ -740,9 +748,9 @@ BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fa
|
|
|
|
|
|
|
|
|
|
mfac = 255 - fac;
|
|
|
|
|
|
|
|
|
|
cp1 = (unsigned char *)&col1;
|
|
|
|
|
cp2 = (unsigned char *)&col2;
|
|
|
|
|
cp = (unsigned char *)&col;
|
|
|
|
|
cp1 = (uchar *)&col1;
|
|
|
|
|
cp2 = (uchar *)&col2;
|
|
|
|
|
cp = (uchar *)&col;
|
|
|
|
|
|
|
|
|
|
/* See if were darker, if so mix, else don't do anything.
|
|
|
|
|
* if the paint col is brighter then the original, then ignore */
|
|
|
|
|
@@ -758,12 +766,14 @@ BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fa
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* wpaint has 'wpaint_blend_tool' */
|
|
|
|
|
static unsigned int vpaint_blend_tool(const int tool, const unsigned int col,
|
|
|
|
|
const unsigned int paintcol, const int alpha_i)
|
|
|
|
|
static uint vpaint_blend_tool(const int tool, const uint col,
|
|
|
|
|
const uint paintcol, const int alpha_i)
|
|
|
|
|
{
|
|
|
|
|
switch (tool) {
|
|
|
|
|
case PAINT_BLEND_MIX:
|
|
|
|
|
case PAINT_BLEND_BLUR: return mcol_blend(col, paintcol, alpha_i);
|
|
|
|
|
case PAINT_BLEND_AVERAGE: return mcol_blend(col, paintcol, alpha_i);
|
|
|
|
|
case PAINT_BLEND_SMEAR: return mcol_blend(col, paintcol, alpha_i);
|
|
|
|
|
case PAINT_BLEND_ADD: return mcol_add(col, paintcol, alpha_i);
|
|
|
|
|
case PAINT_BLEND_SUB: return mcol_sub(col, paintcol, alpha_i);
|
|
|
|
|
case PAINT_BLEND_MUL: return mcol_mul(col, paintcol, alpha_i);
|
|
|
|
|
@@ -776,10 +786,11 @@ static unsigned int vpaint_blend_tool(const int tool, const unsigned int col,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* wpaint has 'wpaint_blend' */
|
|
|
|
|
static unsigned int vpaint_blend(VPaint *vp, unsigned int col, unsigned int colorig, const
|
|
|
|
|
unsigned int paintcol, const int alpha_i,
|
|
|
|
|
/* pre scaled from [0-1] --> [0-255] */
|
|
|
|
|
const int brush_alpha_value_i)
|
|
|
|
|
static uint vpaint_blend(
|
|
|
|
|
VPaint *vp, uint col, uint colorig,
|
|
|
|
|
const uint paintcol, const int alpha_i,
|
|
|
|
|
/* pre scaled from [0-1] --> [0-255] */
|
|
|
|
|
const int brush_alpha_value_i)
|
|
|
|
|
{
|
|
|
|
|
Brush *brush = BKE_paint_brush(&vp->paint);
|
|
|
|
|
const int tool = brush->vertexpaint_tool;
|
|
|
|
|
@@ -788,7 +799,7 @@ static unsigned int vpaint_blend(VPaint *vp, unsigned int col, unsigned int colo
|
|
|
|
|
|
|
|
|
|
/* if no spray, clip color adding with colorig & orig alpha */
|
|
|
|
|
if ((vp->flag & VP_SPRAY) == 0) {
|
|
|
|
|
unsigned int testcol, a;
|
|
|
|
|
uint testcol, a;
|
|
|
|
|
char *cp, *ct, *co;
|
|
|
|
|
|
|
|
|
|
testcol = vpaint_blend_tool(tool, colorig, paintcol, brush_alpha_value_i);
|
|
|
|
|
@@ -813,49 +824,10 @@ static unsigned int vpaint_blend(VPaint *vp, unsigned int col, unsigned int colo
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int sample_backbuf_area(ViewContext *vc, int *indexar, int totpoly, int x, int y, float size)
|
|
|
|
|
{
|
|
|
|
|
struct ImBuf *ibuf;
|
|
|
|
|
int a, tot = 0, index;
|
|
|
|
|
|
|
|
|
|
/* brecht: disabled this because it obviously fails for
|
|
|
|
|
* brushes with size > 64, why is this here? */
|
|
|
|
|
/*if (size > 64.0) size = 64.0;*/
|
|
|
|
|
|
|
|
|
|
ibuf = ED_view3d_backbuf_read(vc, x - size, y - size, x + size, y + size);
|
|
|
|
|
if (ibuf) {
|
|
|
|
|
unsigned int *rt = ibuf->rect;
|
|
|
|
|
|
|
|
|
|
memset(indexar, 0, sizeof(int) * (totpoly + 1));
|
|
|
|
|
|
|
|
|
|
size = ibuf->x * ibuf->y;
|
|
|
|
|
while (size--) {
|
|
|
|
|
|
|
|
|
|
if (*rt) {
|
|
|
|
|
index = *rt;
|
|
|
|
|
if (index > 0 && index <= totpoly) {
|
|
|
|
|
indexar[index] = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rt++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (a = 1; a <= totpoly; a++) {
|
|
|
|
|
if (indexar[a]) {
|
|
|
|
|
indexar[tot++] = a;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMB_freeImBuf(ibuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* whats _dl mean? */
|
|
|
|
|
static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co[3],
|
|
|
|
|
const float mval[2], const float brush_size_pressure, float rgba[4])
|
|
|
|
|
static float calc_vp_strength_col_dl(
|
|
|
|
|
VPaint *vp, const ViewContext *vc, const float co[3],
|
|
|
|
|
const float mval[2], const float brush_size_pressure, float rgba[4])
|
|
|
|
|
{
|
|
|
|
|
float co_ss[2]; /* screenspace */
|
|
|
|
|
|
|
|
|
|
@@ -891,10 +863,11 @@ static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co
|
|
|
|
|
return 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float calc_vp_alpha_col_dl(VPaint *vp, ViewContext *vc,
|
|
|
|
|
float vpimat[3][3], const DMCoNo *v_co_no,
|
|
|
|
|
const float mval[2],
|
|
|
|
|
const float brush_size_pressure, const float brush_alpha_pressure, float rgba[4])
|
|
|
|
|
static float calc_vp_alpha_col_dl(
|
|
|
|
|
VPaint *vp, const ViewContext *vc,
|
|
|
|
|
float vpimat[3][3], const DMCoNo *v_co_no,
|
|
|
|
|
const float mval[2],
|
|
|
|
|
const float brush_size_pressure, const float brush_alpha_pressure, float rgba[4])
|
|
|
|
|
{
|
|
|
|
|
float strength = calc_vp_strength_col_dl(vp, vc, v_co_no->co, mval, brush_size_pressure, rgba);
|
|
|
|
|
|
|
|
|
|
@@ -960,6 +933,8 @@ static float wpaint_blend_tool(const int tool,
|
|
|
|
|
{
|
|
|
|
|
switch (tool) {
|
|
|
|
|
case PAINT_BLEND_MIX:
|
|
|
|
|
case PAINT_BLEND_AVERAGE:
|
|
|
|
|
case PAINT_BLEND_SMEAR:
|
|
|
|
|
case PAINT_BLEND_BLUR: return wval_blend(weight, paintval, alpha);
|
|
|
|
|
case PAINT_BLEND_ADD: return wval_add(weight, paintval, alpha);
|
|
|
|
|
case PAINT_BLEND_SUB: return wval_sub(weight, paintval, alpha);
|
|
|
|
|
@@ -973,9 +948,9 @@ static float wpaint_blend_tool(const int tool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* vpaint has 'vpaint_blend' */
|
|
|
|
|
static float wpaint_blend(VPaint *wp, float weight, float weight_prev,
|
|
|
|
|
static float wpaint_blend(VPaint *wp, float weight,
|
|
|
|
|
const float alpha, float paintval,
|
|
|
|
|
const float brush_alpha_value,
|
|
|
|
|
const float UNUSED(brush_alpha_value),
|
|
|
|
|
const short do_flip)
|
|
|
|
|
{
|
|
|
|
|
Brush *brush = BKE_paint_brush(&wp->paint);
|
|
|
|
|
@@ -1000,21 +975,6 @@ static float wpaint_blend(VPaint *wp, float weight, float weight_prev,
|
|
|
|
|
|
|
|
|
|
CLAMP(weight, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
/* if no spray, clip result with orig weight & orig alpha */
|
|
|
|
|
if ((wp->flag & VP_SPRAY) == 0) {
|
|
|
|
|
float testw = wpaint_blend_tool(tool, weight_prev, paintval, brush_alpha_value);
|
|
|
|
|
|
|
|
|
|
CLAMP(testw, 0.0f, 1.0f);
|
|
|
|
|
if (testw < weight_prev) {
|
|
|
|
|
if (weight < testw) weight = testw;
|
|
|
|
|
else if (weight > weight_prev) weight = weight_prev;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (weight > testw) weight = testw;
|
|
|
|
|
else if (weight < weight_prev) weight = weight_prev;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return weight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1035,7 +995,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
|
|
|
|
|
if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) {
|
|
|
|
|
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
|
|
|
|
int v_idx_best = -1;
|
|
|
|
|
unsigned int index;
|
|
|
|
|
uint index;
|
|
|
|
|
|
|
|
|
|
view3d_operator_needs_opengl(C);
|
|
|
|
|
ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
|
|
|
|
|
@@ -1146,7 +1106,7 @@ static EnumPropertyItem *weight_paint_sample_enum_itemf(
|
|
|
|
|
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
|
|
|
|
int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups");
|
|
|
|
|
bool found = false;
|
|
|
|
|
unsigned int index;
|
|
|
|
|
uint index;
|
|
|
|
|
|
|
|
|
|
const int mval[2] = {
|
|
|
|
|
win->eventstate->x - vc.ar->winrct.xmin,
|
|
|
|
|
@@ -1164,8 +1124,8 @@ static EnumPropertyItem *weight_paint_sample_enum_itemf(
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (ED_mesh_pick_face(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
|
|
|
|
|
MPoly *mp = &me->mpoly[index];
|
|
|
|
|
unsigned int fidx = mp->totloop - 1;
|
|
|
|
|
const MPoly *mp = &me->mpoly[index];
|
|
|
|
|
uint fidx = mp->totloop - 1;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v];
|
|
|
|
|
@@ -1245,7 +1205,7 @@ void PAINT_OT_weight_sample_group(wmOperatorType *ot)
|
|
|
|
|
static void do_weight_paint_normalize_all(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap)
|
|
|
|
|
{
|
|
|
|
|
float sum = 0.0f, fac;
|
|
|
|
|
unsigned int i, tot = 0;
|
|
|
|
|
uint i, tot = 0;
|
|
|
|
|
MDeformWeight *dw;
|
|
|
|
|
|
|
|
|
|
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
|
|
|
|
|
@@ -1291,7 +1251,7 @@ static bool do_weight_paint_normalize_all_locked(
|
|
|
|
|
float sum = 0.0f, fac;
|
|
|
|
|
float sum_unlock = 0.0f;
|
|
|
|
|
float lock_weight = 0.0f;
|
|
|
|
|
unsigned int i, tot = 0;
|
|
|
|
|
uint i, tot = 0;
|
|
|
|
|
MDeformWeight *dw;
|
|
|
|
|
|
|
|
|
|
if (lock_flags == NULL) {
|
|
|
|
|
@@ -1476,7 +1436,7 @@ static void multipaint_apply_change(MDeformVert *dvert, const int defbase_tot, f
|
|
|
|
|
* Variables stored both for 'active' and 'mirror' sides.
|
|
|
|
|
*/
|
|
|
|
|
struct WeightPaintGroupData {
|
|
|
|
|
/** index of active group or its mirror
|
|
|
|
|
/** index of active group or its mirror
|
|
|
|
|
*
|
|
|
|
|
* - 'active' is always `ob->actdef`.
|
|
|
|
|
* - 'mirror' is -1 when 'ME_EDIT_MIRROR_X' flag id disabled,
|
|
|
|
|
@@ -1524,14 +1484,14 @@ static void do_weight_paint_vertex_single(
|
|
|
|
|
/* vars which remain the same for every vert */
|
|
|
|
|
VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
|
|
|
|
|
/* vars which change on each stroke */
|
|
|
|
|
const unsigned int index, float alpha, float paintweight
|
|
|
|
|
const uint index, float alpha, float paintweight
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
MDeformVert *dv = &me->dvert[index];
|
|
|
|
|
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
|
|
|
|
|
|
|
|
|
MDeformWeight *dw, *dw_prev;
|
|
|
|
|
|
|
|
|
|
MDeformWeight *dw;
|
|
|
|
|
|
|
|
|
|
/* mirror vars */
|
|
|
|
|
int index_mirr;
|
|
|
|
|
@@ -1542,14 +1502,12 @@ static void do_weight_paint_vertex_single(
|
|
|
|
|
|
|
|
|
|
if (wp->flag & VP_ONLYVGROUP) {
|
|
|
|
|
dw = defvert_find_index(dv, wpi->active.index);
|
|
|
|
|
dw_prev = defvert_find_index(wp->wpaint_prev + index, wpi->active.index);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dw = defvert_verify_index(dv, wpi->active.index);
|
|
|
|
|
dw_prev = defvert_verify_index(wp->wpaint_prev + index, wpi->active.index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dw == NULL || dw_prev == NULL) {
|
|
|
|
|
if (dw == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1607,7 +1565,7 @@ static void do_weight_paint_vertex_single(
|
|
|
|
|
* then there is no need to run the more complicated checks */
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
dw->weight = wpaint_blend(wp, dw->weight, dw_prev->weight, alpha, paintweight,
|
|
|
|
|
dw->weight = wpaint_blend(wp, dw->weight, alpha, paintweight,
|
|
|
|
|
wpi->brush_alpha_value, wpi->do_flip);
|
|
|
|
|
|
|
|
|
|
/* WATCH IT: take care of the ordering of applying mirror -> normalize,
|
|
|
|
|
@@ -1669,11 +1627,10 @@ static void do_weight_paint_vertex_multi(
|
|
|
|
|
/* vars which remain the same for every vert */
|
|
|
|
|
VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
|
|
|
|
|
/* vars which change on each stroke */
|
|
|
|
|
const unsigned int index, float alpha, float paintweight)
|
|
|
|
|
const uint index, float alpha, float paintweight)
|
|
|
|
|
{
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
MDeformVert *dv = &me->dvert[index];
|
|
|
|
|
MDeformVert *dv_prev = &wp->wpaint_prev[index];
|
|
|
|
|
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
|
|
|
|
|
|
|
|
|
/* mirror vars */
|
|
|
|
|
@@ -1681,7 +1638,7 @@ static void do_weight_paint_vertex_multi(
|
|
|
|
|
MDeformVert *dv_mirr = NULL;
|
|
|
|
|
|
|
|
|
|
/* weights */
|
|
|
|
|
float oldw, curw, neww, change, curw_mirr, change_mirr;
|
|
|
|
|
float curw, neww, change, curw_mirr, change_mirr;
|
|
|
|
|
|
|
|
|
|
/* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */
|
|
|
|
|
if (me->editflag & ME_EDIT_MIRROR_X) {
|
|
|
|
|
@@ -1693,8 +1650,6 @@ static void do_weight_paint_vertex_multi(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* compute weight change by applying the brush to average or sum of group weights */
|
|
|
|
|
oldw = BKE_defvert_multipaint_collective_weight(
|
|
|
|
|
dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
|
|
|
|
|
curw = BKE_defvert_multipaint_collective_weight(
|
|
|
|
|
dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
|
|
|
|
|
|
|
|
|
|
@@ -1703,7 +1658,7 @@ static void do_weight_paint_vertex_multi(
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
neww = wpaint_blend(wp, curw, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip);
|
|
|
|
|
neww = wpaint_blend(wp, curw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip);
|
|
|
|
|
|
|
|
|
|
change = neww / curw;
|
|
|
|
|
|
|
|
|
|
@@ -1759,7 +1714,7 @@ static void do_weight_paint_vertex(
|
|
|
|
|
/* vars which remain the same for every vert */
|
|
|
|
|
VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
|
|
|
|
|
/* vars which change on each stroke */
|
|
|
|
|
const unsigned int index, float alpha, float paintweight)
|
|
|
|
|
const uint index, float alpha, float paintweight)
|
|
|
|
|
{
|
|
|
|
|
if (wpi->do_multipaint) {
|
|
|
|
|
do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight);
|
|
|
|
|
@@ -1769,6 +1724,82 @@ static void do_weight_paint_vertex(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Toggle operator for turning vertex paint mode on or off (copied from sculpt.c) */
|
|
|
|
|
static void vertex_paint_init_session(Scene *scene, Object *ob)
|
|
|
|
|
{
|
|
|
|
|
if (ob->sculpt == NULL) {
|
|
|
|
|
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
|
|
|
|
|
BKE_sculpt_update_mesh_elements(scene, scene->toolsettings->sculpt, ob, 0, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
|
|
|
|
|
{
|
|
|
|
|
/* Create maps */
|
|
|
|
|
struct SculptVertexPaintGeomMap *gmap = NULL;
|
|
|
|
|
if (ob->mode == OB_MODE_VERTEX_PAINT) {
|
|
|
|
|
gmap = &ob->sculpt->mode.vpaint.gmap;
|
|
|
|
|
ob->sculpt->mode_type = OB_MODE_VERTEX_PAINT;
|
|
|
|
|
}
|
|
|
|
|
else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
|
|
|
|
|
gmap = &ob->sculpt->mode.wpaint.gmap;
|
|
|
|
|
ob->sculpt->mode_type = OB_MODE_WEIGHT_PAINT;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ob->sculpt->mode_type = 0;
|
|
|
|
|
BLI_assert(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
|
|
|
|
|
if (gmap->vert_to_loop == NULL) {
|
|
|
|
|
gmap->vert_map_mem = NULL;
|
|
|
|
|
gmap->vert_to_loop = NULL;
|
|
|
|
|
gmap->poly_map_mem = NULL;
|
|
|
|
|
gmap->vert_to_poly = NULL;
|
|
|
|
|
BKE_mesh_vert_loop_map_create(
|
|
|
|
|
&gmap->vert_to_loop,
|
|
|
|
|
&gmap->vert_map_mem,
|
|
|
|
|
me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
|
|
|
|
|
BKE_mesh_vert_poly_map_create(
|
|
|
|
|
&gmap->vert_to_poly,
|
|
|
|
|
&gmap->poly_map_mem,
|
|
|
|
|
me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create average brush arrays */
|
|
|
|
|
if (ob->mode == OB_MODE_VERTEX_PAINT) {
|
|
|
|
|
if ((ts->vpaint->flag & VP_SPRAY) == 0) {
|
|
|
|
|
if (ob->sculpt->mode.vpaint.previous_color == NULL) {
|
|
|
|
|
ob->sculpt->mode.vpaint.previous_color =
|
|
|
|
|
MEM_callocN(me->totloop * sizeof(uint), "previous_color");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
|
|
|
|
|
if ((ts->wpaint->flag & VP_SPRAY) == 0) {
|
|
|
|
|
if (ob->sculpt->mode.wpaint.alpha_weight == NULL) {
|
|
|
|
|
ob->sculpt->mode.wpaint.alpha_weight =
|
|
|
|
|
MEM_callocN(me->totvert * sizeof(float), "alpha_weight");
|
|
|
|
|
}
|
|
|
|
|
if (ob->sculpt->mode.wpaint.previous_weight == NULL) {
|
|
|
|
|
ob->sculpt->mode.wpaint.previous_weight =
|
|
|
|
|
MEM_mallocN(me->totvert * sizeof(float), "previous_weight");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight);
|
|
|
|
|
MEM_SAFE_FREE(ob->sculpt->mode.wpaint.previous_weight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* *************** set wpaint operator ****************** */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@@ -1805,6 +1836,14 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|
|
|
|
ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e');
|
|
|
|
|
ED_mesh_mirror_topo_table(NULL, NULL, 'e');
|
|
|
|
|
|
|
|
|
|
/* If the cache is not released by a cancel or a done, free it now. */
|
|
|
|
|
if (ob->sculpt->cache) {
|
|
|
|
|
sculpt_cache_free(ob->sculpt->cache);
|
|
|
|
|
ob->sculpt->cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_sculptsession_free(ob);
|
|
|
|
|
|
|
|
|
|
paint_cursor_delete_textures();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
@@ -1820,6 +1859,12 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|
|
|
|
/* weight paint specific */
|
|
|
|
|
ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 's');
|
|
|
|
|
ED_vgroup_sync_from_pose(ob);
|
|
|
|
|
|
|
|
|
|
/* Create vertex/weight paint mode session data */
|
|
|
|
|
if (ob->sculpt) {
|
|
|
|
|
BKE_sculptsession_free(ob);
|
|
|
|
|
}
|
|
|
|
|
vertex_paint_init_session(scene, ob);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Weightpaint works by overriding colors in mesh,
|
|
|
|
|
@@ -1877,7 +1922,6 @@ struct WPaintVGroupIndex {
|
|
|
|
|
|
|
|
|
|
struct WPaintData {
|
|
|
|
|
ViewContext vc;
|
|
|
|
|
int *indexar;
|
|
|
|
|
|
|
|
|
|
struct WeightPaintGroupData active, mirror;
|
|
|
|
|
|
|
|
|
|
@@ -1895,14 +1939,6 @@ struct WPaintData {
|
|
|
|
|
int defbase_tot_sel; /* number of selected groups */
|
|
|
|
|
bool do_multipaint; /* true if multipaint enabled and multiple groups selected */
|
|
|
|
|
|
|
|
|
|
/* variables for blur */
|
|
|
|
|
struct {
|
|
|
|
|
MeshElemMap *vmap;
|
|
|
|
|
int *vmap_mem;
|
|
|
|
|
} blur_data;
|
|
|
|
|
|
|
|
|
|
BLI_Stack *accumulate_stack; /* for reuse (WPaintDefer) */
|
|
|
|
|
|
|
|
|
|
int defbase_tot;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -1982,19 +2018,129 @@ static bool wpaint_ensure_data(
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float UNUSED(mouse[2]))
|
|
|
|
|
/* Initialize the stroke cache invariants from operator properties */
|
|
|
|
|
static void vwpaint_update_cache_invariants(
|
|
|
|
|
bContext *C, VPaint *vd, SculptSession *ss, wmOperator *op, const float mouse[2])
|
|
|
|
|
{
|
|
|
|
|
StrokeCache *cache;
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
|
|
|
|
|
Brush *brush = BKE_paint_brush(&vd->paint);
|
|
|
|
|
ViewContext *vc = paint_stroke_view_context(op->customdata);
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
float mat[3][3];
|
|
|
|
|
float view_dir[3] = {0.0f, 0.0f, 1.0f};
|
|
|
|
|
int mode;
|
|
|
|
|
|
|
|
|
|
/* VW paint needs to allocate stroke cache before update is called. */
|
|
|
|
|
if (!ss->cache) {
|
|
|
|
|
cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
|
|
|
|
|
ss->cache = cache;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cache = ss->cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initial mouse location */
|
|
|
|
|
if (mouse)
|
|
|
|
|
copy_v2_v2(cache->initial_mouse, mouse);
|
|
|
|
|
else
|
|
|
|
|
zero_v2(cache->initial_mouse);
|
|
|
|
|
|
|
|
|
|
mode = RNA_enum_get(op->ptr, "mode");
|
|
|
|
|
cache->invert = mode == BRUSH_STROKE_INVERT;
|
|
|
|
|
cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH;
|
|
|
|
|
/* not very nice, but with current events system implementation
|
|
|
|
|
* we can't handle brush appearance inversion hotkey separately (sergey) */
|
|
|
|
|
if (cache->invert) ups->draw_inverted = true;
|
|
|
|
|
else ups->draw_inverted = false;
|
|
|
|
|
|
|
|
|
|
copy_v2_v2(cache->mouse, cache->initial_mouse);
|
|
|
|
|
/* Truly temporary data that isn't stored in properties */
|
|
|
|
|
cache->vc = vc;
|
|
|
|
|
cache->brush = brush;
|
|
|
|
|
cache->first_time = 1;
|
|
|
|
|
|
|
|
|
|
/* cache projection matrix */
|
|
|
|
|
ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat);
|
|
|
|
|
|
|
|
|
|
invert_m4_m4(ob->imat, ob->obmat);
|
|
|
|
|
copy_m3_m4(mat, cache->vc->rv3d->viewinv);
|
|
|
|
|
mul_m3_v3(mat, view_dir);
|
|
|
|
|
copy_m3_m4(mat, ob->imat);
|
|
|
|
|
mul_m3_v3(mat, view_dir);
|
|
|
|
|
normalize_v3_v3(cache->true_view_normal, view_dir);
|
|
|
|
|
|
|
|
|
|
copy_v3_v3(cache->view_normal, cache->true_view_normal);
|
|
|
|
|
cache->bstrength = BKE_brush_alpha_get(scene, brush);
|
|
|
|
|
cache->is_last_valid = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize the stroke cache variants from operator properties */
|
|
|
|
|
static void vwpaint_update_cache_variants(bContext *C, VPaint *vd, Object *ob, PointerRNA *ptr)
|
|
|
|
|
{
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
Brush *brush = BKE_paint_brush(&vd->paint);
|
|
|
|
|
|
|
|
|
|
/* This effects the actual brush radius, so things farther away
|
|
|
|
|
* are compared with a larger radius and vise versa. */
|
|
|
|
|
if (cache->first_time) {
|
|
|
|
|
RNA_float_get_array(ptr, "location", cache->true_location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RNA_float_get_array(ptr, "mouse", cache->mouse);
|
|
|
|
|
|
|
|
|
|
/* XXX: Use pressure value from first brush step for brushes which don't
|
|
|
|
|
* support strokes (grab, thumb). They depends on initial state and
|
|
|
|
|
* brush coord/pressure/etc.
|
|
|
|
|
* It's more an events design issue, which doesn't split coordinate/pressure/angle
|
|
|
|
|
* changing events. We should avoid this after events system re-design */
|
|
|
|
|
if (paint_supports_dynamic_size(brush, ePaintSculpt) || cache->first_time) {
|
|
|
|
|
cache->pressure = RNA_float_get(ptr, "pressure");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Truly temporary data that isn't stored in properties */
|
|
|
|
|
if (cache->first_time) {
|
|
|
|
|
if (!BKE_brush_use_locked_size(scene, brush)) {
|
|
|
|
|
cache->initial_radius = paint_calc_object_space_radius(
|
|
|
|
|
cache->vc, cache->true_location, BKE_brush_size_get(scene, brush));
|
|
|
|
|
BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, ePaintSculpt)) {
|
|
|
|
|
cache->radius = cache->initial_radius * cache->pressure;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cache->radius = cache->initial_radius;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cache->radius_squared = cache->radius * cache->radius;
|
|
|
|
|
|
|
|
|
|
if (ss->pbvh) {
|
|
|
|
|
BKE_pbvh_update(ss->pbvh, PBVH_UpdateRedraw, NULL);
|
|
|
|
|
BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
|
|
|
|
|
{
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
struct PaintStroke *stroke = op->customdata;
|
|
|
|
|
ToolSettings *ts = scene->toolsettings;
|
|
|
|
|
VPaint *wp = ts->wpaint;
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
Mesh *me = BKE_mesh_from_object(ob);
|
|
|
|
|
struct WPaintData *wpd;
|
|
|
|
|
struct WPaintVGroupIndex vgroup_index;
|
|
|
|
|
int defbase_tot, defbase_tot_sel;
|
|
|
|
|
bool *defbase_sel;
|
|
|
|
|
const Brush *brush = BKE_paint_brush(&wp->paint);
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
VPaint *vd = CTX_data_tool_settings(C)->wpaint;
|
|
|
|
|
|
|
|
|
|
float mat[4][4], imat[4][4];
|
|
|
|
|
|
|
|
|
|
@@ -2094,62 +2240,598 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float UN
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* painting on subsurfs should give correct points too, this returns me->totvert amount */
|
|
|
|
|
ob->sculpt->building_vp_handle = true;
|
|
|
|
|
wpd->vp_handle = ED_vpaint_proj_handle_create(scene, ob, &wpd->vertexcosnos);
|
|
|
|
|
|
|
|
|
|
wpd->indexar = get_indexarray(me);
|
|
|
|
|
copy_wpaint_prev(wp, me->dvert, me->totvert);
|
|
|
|
|
|
|
|
|
|
if (brush->vertexpaint_tool == PAINT_BLEND_BLUR) {
|
|
|
|
|
BKE_mesh_vert_edge_vert_map_create(
|
|
|
|
|
&wpd->blur_data.vmap, &wpd->blur_data.vmap_mem,
|
|
|
|
|
me->medge, me->totvert, me->totedge);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((brush->vertexpaint_tool == PAINT_BLEND_BLUR) &&
|
|
|
|
|
(brush->flag & BRUSH_ACCUMULATE))
|
|
|
|
|
{
|
|
|
|
|
wpd->accumulate_stack = BLI_stack_new(sizeof(struct WPaintDefer), __func__);
|
|
|
|
|
}
|
|
|
|
|
ob->sculpt->building_vp_handle = false;
|
|
|
|
|
|
|
|
|
|
/* imat for normals */
|
|
|
|
|
mul_m4_m4m4(mat, wpd->vc.rv3d->viewmat, ob->obmat);
|
|
|
|
|
invert_m4_m4(imat, mat);
|
|
|
|
|
copy_m3_m4(wpd->wpimat, imat);
|
|
|
|
|
|
|
|
|
|
/* If not previously created, create vertex/weight paint mode session data */
|
|
|
|
|
vertex_paint_init_session(scene, ob);
|
|
|
|
|
vwpaint_update_cache_invariants(C, vd, ss, op, mouse);
|
|
|
|
|
vertex_paint_init_session_data(ts, ob);
|
|
|
|
|
|
|
|
|
|
if (ss->mode.wpaint.previous_weight != NULL) {
|
|
|
|
|
copy_vn_fl(ss->mode.wpaint.previous_weight, me->totvert, -1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float wpaint_blur_weight_single(const MDeformVert *dv, const WeightPaintInfo *wpi)
|
|
|
|
|
static void calc_area_normal_and_center_task_cb(void *userdata, const int n)
|
|
|
|
|
{
|
|
|
|
|
return defvert_find_weight(dv, wpi->active.index);
|
|
|
|
|
}
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
float(*area_nos)[3] = data->area_nos;
|
|
|
|
|
float(*area_cos)[3] = data->area_cos;
|
|
|
|
|
|
|
|
|
|
static float wpaint_blur_weight_multi(const MDeformVert *dv, const WeightPaintInfo *wpi)
|
|
|
|
|
{
|
|
|
|
|
float weight = BKE_defvert_multipaint_collective_weight(
|
|
|
|
|
dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
|
|
|
|
|
CLAMP(weight, 0.0f, 1.0f);
|
|
|
|
|
return weight;
|
|
|
|
|
}
|
|
|
|
|
float private_co[2][3] = {{0.0f}};
|
|
|
|
|
float private_no[2][3] = {{0.0f}};
|
|
|
|
|
int private_count[2] = {0};
|
|
|
|
|
|
|
|
|
|
static float wpaint_blur_weight_calc_from_connected(
|
|
|
|
|
const MDeformVert *dvert, WeightPaintInfo *wpi, struct WPaintData *wpd, const unsigned int vidx,
|
|
|
|
|
float (*blur_weight_func)(const MDeformVert *, const WeightPaintInfo *))
|
|
|
|
|
{
|
|
|
|
|
const MeshElemMap *map = &wpd->blur_data.vmap[vidx];
|
|
|
|
|
float paintweight;
|
|
|
|
|
if (map->count != 0) {
|
|
|
|
|
paintweight = 0.0f;
|
|
|
|
|
for (int j = 0; j < map->count; j++) {
|
|
|
|
|
paintweight += blur_weight_func(&dvert[map->indices[j]], wpi);
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
const float *co;
|
|
|
|
|
|
|
|
|
|
co = vd.co;
|
|
|
|
|
|
|
|
|
|
if (sculpt_brush_test_fast(&test, co)) {
|
|
|
|
|
float no_buf[3];
|
|
|
|
|
const float *no;
|
|
|
|
|
int flip_index;
|
|
|
|
|
|
|
|
|
|
if (vd.no) {
|
|
|
|
|
normal_short_to_float_v3(no_buf, vd.no);
|
|
|
|
|
no = no_buf;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
no = vd.fno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f);
|
|
|
|
|
if (area_cos)
|
|
|
|
|
add_v3_v3(private_co[flip_index], co);
|
|
|
|
|
if (area_nos)
|
|
|
|
|
add_v3_v3(private_no[flip_index], no);
|
|
|
|
|
private_count[flip_index] += 1;
|
|
|
|
|
}
|
|
|
|
|
paintweight /= map->count;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
paintweight = blur_weight_func(&dvert[vidx], wpi);
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BLI_mutex_lock(&data->mutex);
|
|
|
|
|
|
|
|
|
|
/* for flatten center */
|
|
|
|
|
if (area_cos) {
|
|
|
|
|
add_v3_v3(area_cos[0], private_co[0]);
|
|
|
|
|
add_v3_v3(area_cos[1], private_co[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return paintweight;
|
|
|
|
|
/* for area normal */
|
|
|
|
|
if (area_nos) {
|
|
|
|
|
add_v3_v3(area_nos[0], private_no[0]);
|
|
|
|
|
add_v3_v3(area_nos[1], private_no[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* weights */
|
|
|
|
|
data->count[0] += private_count[0];
|
|
|
|
|
data->count[1] += private_count[1];
|
|
|
|
|
|
|
|
|
|
BLI_mutex_unlock(&data->mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void calc_area_normal(
|
|
|
|
|
VPaint *vp, Object *ob,
|
|
|
|
|
PBVHNode **nodes, int totnode,
|
|
|
|
|
float r_area_no[3])
|
|
|
|
|
{
|
|
|
|
|
/* 0=towards view, 1=flipped */
|
|
|
|
|
float area_nos[2][3] = {{0.0f}};
|
|
|
|
|
|
|
|
|
|
int count[2] = {0};
|
|
|
|
|
|
|
|
|
|
SculptThreadedTaskData data = {
|
|
|
|
|
.vp = vp, .ob = ob, .nodes = nodes, .totnode = totnode,
|
|
|
|
|
.area_cos = NULL, .area_nos = area_nos, .count = count,
|
|
|
|
|
};
|
|
|
|
|
BLI_mutex_init(&data.mutex);
|
|
|
|
|
|
|
|
|
|
BLI_task_parallel_range(
|
|
|
|
|
0, totnode, &data, calc_area_normal_and_center_task_cb, true);
|
|
|
|
|
|
|
|
|
|
BLI_mutex_end(&data.mutex);
|
|
|
|
|
|
|
|
|
|
/* for area normal */
|
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(area_nos); i++) {
|
|
|
|
|
if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float dot_vf3vs3(const float brushNormal[3], const short vertexNormal[3])
|
|
|
|
|
{
|
|
|
|
|
float normal[3];
|
|
|
|
|
normal_short_to_float_v3(normal, vertexNormal);
|
|
|
|
|
return dot_v3v3(brushNormal, normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Flip all the editdata across the axis/axes specified by symm. Used to
|
|
|
|
|
* calculate multiple modifications to the mesh when symmetry is enabled. */
|
|
|
|
|
static void calc_brushdata_symm(
|
|
|
|
|
VPaint *vd, StrokeCache *cache, const char symm,
|
|
|
|
|
const char axis, const float angle)
|
|
|
|
|
{
|
|
|
|
|
(void)vd; /* unused */
|
|
|
|
|
|
|
|
|
|
flip_v3_v3(cache->location, cache->true_location, symm);
|
|
|
|
|
flip_v3_v3(cache->last_location, cache->true_last_location, symm);
|
|
|
|
|
flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm);
|
|
|
|
|
flip_v3_v3(cache->view_normal, cache->true_view_normal, symm);
|
|
|
|
|
|
|
|
|
|
unit_m4(cache->symm_rot_mat);
|
|
|
|
|
unit_m4(cache->symm_rot_mat_inv);
|
|
|
|
|
zero_v3(cache->plane_offset);
|
|
|
|
|
|
|
|
|
|
if (axis) { /* expects XYZ */
|
|
|
|
|
rotate_m4(cache->symm_rot_mat, axis, angle);
|
|
|
|
|
rotate_m4(cache->symm_rot_mat_inv, axis, -angle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mul_m4_v3(cache->symm_rot_mat, cache->location);
|
|
|
|
|
mul_m4_v3(cache->symm_rot_mat, cache->last_location);
|
|
|
|
|
mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry);
|
|
|
|
|
|
|
|
|
|
if (cache->supports_gravity) {
|
|
|
|
|
flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm);
|
|
|
|
|
mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cache->is_rake_rotation_valid) {
|
|
|
|
|
flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void get_brush_alpha_data(
|
|
|
|
|
Scene *scene, SculptSession *ss, Brush *brush,
|
|
|
|
|
float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure)
|
|
|
|
|
{
|
|
|
|
|
*r_brush_size_pressure =
|
|
|
|
|
BKE_brush_size_get(scene, brush) *
|
|
|
|
|
(BKE_brush_use_size_pressure(scene, brush) ? ss->cache->pressure : 1.0f);
|
|
|
|
|
*r_brush_alpha_value =
|
|
|
|
|
BKE_brush_alpha_get(scene, brush);
|
|
|
|
|
*r_brush_alpha_pressure =
|
|
|
|
|
*r_brush_alpha_value *
|
|
|
|
|
(BKE_brush_use_alpha_pressure(scene, brush) ? ss->cache->pressure : 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_wpaint_brush_blur_task_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
|
|
|
|
|
|
|
|
|
|
Brush *brush = data->brush;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
Scene *scene = CTX_data_scene(data->C);
|
|
|
|
|
|
|
|
|
|
const float brush_strength = cache->bstrength;
|
|
|
|
|
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
|
|
|
|
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
/* For each vertex */
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test_sq(&test, vd.co)) {
|
|
|
|
|
/* For grid based pbvh, take the vert whose loop coopresponds to the current grid.
|
|
|
|
|
* Otherwise, take the current vert. */
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
|
|
|
|
|
const char v_flag = data->me->mvert[v_index].flag;
|
|
|
|
|
/* If the vertex is selected */
|
|
|
|
|
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
|
|
|
|
|
/* Get the average poly weight */
|
|
|
|
|
int total_hit_loops = 0;
|
|
|
|
|
float weight_final = 0.0f;
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
|
|
|
|
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
|
|
|
|
const MPoly *mp = &data->me->mpoly[p_index];
|
|
|
|
|
|
|
|
|
|
total_hit_loops += mp->totloop;
|
|
|
|
|
for (int k = 0; k < mp->totloop; k++) {
|
|
|
|
|
const int l_index = mp->loopstart + k;
|
|
|
|
|
const MLoop *ml = &data->me->mloop[l_index];
|
|
|
|
|
const MDeformVert *dv = &data->me->dvert[ml->v];
|
|
|
|
|
weight_final += defvert_find_weight(dv, data->wpi->active.index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Apply the weight to the vertex. */
|
|
|
|
|
if (total_hit_loops != 0) {
|
|
|
|
|
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
|
|
|
|
|
if (view_dot > 0.0f) {
|
|
|
|
|
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
|
|
|
|
|
const float final_alpha =
|
|
|
|
|
view_dot * brush_fade * brush_strength *
|
|
|
|
|
grid_alpha * brush_alpha_pressure;
|
|
|
|
|
weight_final /= total_hit_loops;
|
|
|
|
|
|
|
|
|
|
/* Only paint visable verts */
|
|
|
|
|
do_weight_paint_vertex(
|
|
|
|
|
data->vp, data->ob, data->wpi,
|
|
|
|
|
v_index, final_alpha, weight_final);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_wpaint_brush_smear_task_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
|
|
|
|
|
|
|
|
|
|
Brush *brush = data->brush;
|
|
|
|
|
Scene *scene = CTX_data_scene(data->C);
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
const float brush_strength = cache->bstrength;
|
|
|
|
|
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
|
|
|
|
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
|
|
|
|
float brush_dir[3];
|
|
|
|
|
|
|
|
|
|
sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
|
|
|
|
|
if (normalize_v3(brush_dir) != 0.0f) {
|
|
|
|
|
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
/* For each vertex */
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test_fast(&test, vd.co)) {
|
|
|
|
|
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
|
|
|
|
|
if (view_dot > 0.0f) {
|
|
|
|
|
bool do_color = false;
|
|
|
|
|
|
|
|
|
|
/* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
|
|
|
|
|
* Otherwise, take the current vert. */
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
|
|
|
|
|
const MVert *mv_curr = &data->me->mvert[v_index];
|
|
|
|
|
const char v_flag = data->me->mvert[v_index].flag;
|
|
|
|
|
|
|
|
|
|
/* If the vertex is selected */
|
|
|
|
|
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
|
|
|
|
|
/* Minimum dot product between brush direction and current
|
|
|
|
|
* to neighbor direction is 0.0, meaning orthogonal. */
|
|
|
|
|
float stroke_dot_max = 0.0f;
|
|
|
|
|
|
|
|
|
|
/* Get the color of the loop in the opposite direction of the brush movement
|
|
|
|
|
* (this callback is specifically for smear.) */
|
|
|
|
|
float weight_final = 0.0;
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
|
|
|
|
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
|
|
|
|
const MPoly *mp = &data->me->mpoly[p_index];
|
|
|
|
|
for (int k = 0; k < mp->totloop; k++) {
|
|
|
|
|
const uint l_index = mp->loopstart + k;
|
|
|
|
|
const MLoop *ml = &data->me->mloop[l_index];
|
|
|
|
|
const uint v_other_index = ml->v;
|
|
|
|
|
const MVert *mv_other = &data->me->mvert[v_other_index];
|
|
|
|
|
|
|
|
|
|
/* Get the direction from the selected vert to the neighbor. */
|
|
|
|
|
float other_dir[3];
|
|
|
|
|
sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co);
|
|
|
|
|
normalize_v3(other_dir);
|
|
|
|
|
|
|
|
|
|
const float stroke_dot = dot_v3v3(other_dir, brush_dir);
|
|
|
|
|
|
|
|
|
|
if (stroke_dot > stroke_dot_max) {
|
|
|
|
|
stroke_dot_max = stroke_dot;
|
|
|
|
|
MDeformVert *dv = &data->me->dvert[v_other_index];
|
|
|
|
|
weight_final = defvert_find_weight(dv, data->wpi->active.index);
|
|
|
|
|
do_color = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Apply weight to vertex */
|
|
|
|
|
if (do_color) {
|
|
|
|
|
const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
|
|
|
|
|
const float final_alpha =
|
|
|
|
|
view_dot * brush_fade * brush_strength *
|
|
|
|
|
grid_alpha * brush_alpha_pressure;
|
|
|
|
|
do_weight_paint_vertex(
|
|
|
|
|
data->vp, data->ob, data->wpi,
|
|
|
|
|
v_index, final_alpha, (float)weight_final);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_wpaint_brush_draw_task_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
Scene *scene = CTX_data_scene(data->C);
|
|
|
|
|
|
|
|
|
|
Brush *brush = data->brush;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
const float brush_strength = cache->bstrength;
|
|
|
|
|
const float paintweight = BKE_brush_weight_get(scene, brush);
|
|
|
|
|
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
|
|
|
|
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
/* For each vertex */
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test_sq(&test, vd.co)) {
|
|
|
|
|
/* Note: grids are 1:1 with corners (aka loops).
|
|
|
|
|
* For multires, take the vert whose loop cooresponds to the current grid.
|
|
|
|
|
* Otherwise, take the current vert. */
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
|
|
|
|
|
|
|
|
|
|
const char v_flag = data->me->mvert[v_index].flag;
|
|
|
|
|
/* If the vertex is selected */
|
|
|
|
|
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
|
|
|
|
|
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
|
|
|
|
|
if (view_dot > 0.0f) {
|
|
|
|
|
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
|
|
|
|
|
float final_alpha = view_dot * brush_fade * brush_strength * grid_alpha * brush_alpha_pressure;
|
|
|
|
|
|
|
|
|
|
/* Non-spray logic. */
|
|
|
|
|
if ((data->vp->flag & VP_SPRAY) == 0) {
|
|
|
|
|
/* Only paint if we have greater alpha. */
|
|
|
|
|
if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) {
|
|
|
|
|
ss->mode.wpaint.alpha_weight[v_index] = final_alpha;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MDeformVert *dv = &data->me->dvert[v_index];
|
|
|
|
|
MDeformWeight *dw = defvert_find_index(dv, data->wpi->active.index);
|
|
|
|
|
float *weight_prev = &ss->mode.wpaint.previous_weight[v_index];
|
|
|
|
|
defweight_prev_init(dw, weight_prev);
|
|
|
|
|
if (dw) {
|
|
|
|
|
dw->weight = *weight_prev;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do_weight_paint_vertex(
|
|
|
|
|
data->vp, data->ob, data->wpi,
|
|
|
|
|
v_index, final_alpha, paintweight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_wpaint_brush_calc_average_weight_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
struct WPaintAverageAccum *accum = (struct WPaintAverageAccum *)data->custom_data + n;
|
|
|
|
|
accum->len = 0;
|
|
|
|
|
accum->value = 0.0;
|
|
|
|
|
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
/* For each vertex */
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test_sq(&test, vd.co)) {
|
|
|
|
|
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
|
|
|
|
|
if (view_dot > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) {
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
// const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
|
|
|
|
|
const char v_flag = data->me->mvert[v_index].flag;
|
|
|
|
|
|
|
|
|
|
/* If the vertex is selected. */
|
|
|
|
|
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
|
|
|
|
|
const MDeformVert *dv = &data->me->dvert[v_index];
|
|
|
|
|
accum->len += 1;
|
|
|
|
|
accum->value += defvert_find_weight(dv, data->wpi->active.index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void calculate_average_weight(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode)
|
|
|
|
|
{
|
|
|
|
|
Scene *scene = CTX_data_scene(data->C);
|
|
|
|
|
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
|
|
|
|
|
|
|
|
|
|
struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__);
|
|
|
|
|
data->custom_data = accum;
|
|
|
|
|
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, data, NULL, 0, do_wpaint_brush_calc_average_weight_cb_ex,
|
|
|
|
|
((data->sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT), false);
|
|
|
|
|
|
|
|
|
|
uint accum_len = 0;
|
|
|
|
|
double accum_weight = 0.0;
|
|
|
|
|
for (int i = 0; i < totnode; i++) {
|
|
|
|
|
accum_len += accum[i].len;
|
|
|
|
|
accum_weight += accum[i].value;
|
|
|
|
|
}
|
|
|
|
|
if (accum_len != 0) {
|
|
|
|
|
accum_weight /= accum_len;
|
|
|
|
|
if (ups->flag & UNIFIED_PAINT_WEIGHT)
|
|
|
|
|
ups->weight = (float)accum_weight;
|
|
|
|
|
else
|
|
|
|
|
data->brush->weight = (float)accum_weight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(data->custom_data); /* 'accum' */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void wpaint_paint_leaves(
|
|
|
|
|
bContext *C, Object *ob, Sculpt *sd, VPaint *vp, struct WPaintData *wpd, WeightPaintInfo *wpi,
|
|
|
|
|
Mesh *me, PBVHNode **nodes, int totnode)
|
|
|
|
|
{
|
|
|
|
|
Brush *brush = ob->sculpt->cache->brush;
|
|
|
|
|
|
|
|
|
|
/* threaded loop over nodes */
|
|
|
|
|
SculptThreadedTaskData data = {
|
|
|
|
|
.sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .vp = vp, .wpd = wpd, .wpi = wpi, .me = me, .C = C,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switch (brush->vertexpaint_tool) {
|
|
|
|
|
case PAINT_BLEND_AVERAGE:
|
|
|
|
|
calculate_average_weight(&data, nodes, totnode);
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_wpaint_brush_draw_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
case PAINT_BLEND_SMEAR:
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_wpaint_brush_smear_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
case PAINT_BLEND_BLUR:
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_wpaint_brush_blur_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_wpaint_brush_draw_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wpaint_do_paint(
|
|
|
|
|
bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi,
|
|
|
|
|
Mesh *me, Brush *UNUSED(brush), const char symm, const int axis, const int i, const float angle)
|
|
|
|
|
{
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
ss->cache->radial_symmetry_pass = i;
|
|
|
|
|
calc_brushdata_symm(wp, ss->cache, symm, axis, angle);
|
|
|
|
|
|
|
|
|
|
SculptSearchSphereData data;
|
|
|
|
|
PBVHNode **nodes = NULL;
|
|
|
|
|
int totnode;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Build a list of all nodes that are potentially within the brush's area of influence */
|
|
|
|
|
data.ss = ss;
|
|
|
|
|
data.sd = sd;
|
|
|
|
|
data.radius_squared = ss->cache->radius_squared;
|
|
|
|
|
data.original = true;
|
|
|
|
|
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
|
|
|
|
|
|
|
|
|
|
calc_area_normal(wp, ob, nodes, totnode, ss->cache->sculpt_normal_symm);
|
|
|
|
|
wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode);
|
|
|
|
|
|
|
|
|
|
if (nodes)
|
|
|
|
|
MEM_freeN(nodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wpaint_do_radial_symmetry(
|
|
|
|
|
bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi,
|
|
|
|
|
Mesh *me, Brush *brush, const char symm, const int axis)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 1; i < wp->radial_symm[axis - 'X']; i++) {
|
|
|
|
|
const float angle = (2.0 * M_PI) * i / wp->radial_symm[axis - 'X'];
|
|
|
|
|
wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, symm, axis, i, angle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wpaint_do_symmetrical_brush_actions(
|
|
|
|
|
bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi)
|
|
|
|
|
{
|
|
|
|
|
Brush *brush = BKE_paint_brush(&wp->paint);
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
const char symm = wp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
/* initial stroke */
|
|
|
|
|
wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X', 0, 0);
|
|
|
|
|
wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X');
|
|
|
|
|
wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Y');
|
|
|
|
|
wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Z');
|
|
|
|
|
|
|
|
|
|
cache->symmetry = symm;
|
|
|
|
|
|
|
|
|
|
/* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
|
|
|
|
|
for (i = 1; i <= symm; i++) {
|
|
|
|
|
if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
|
|
|
|
|
cache->mirror_symmetry_pass = i;
|
|
|
|
|
cache->radial_symmetry_pass = 0;
|
|
|
|
|
calc_brushdata_symm(wp, cache, i, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (i & (1 << 0)) {
|
|
|
|
|
wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X', 0, 0);
|
|
|
|
|
wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X');
|
|
|
|
|
}
|
|
|
|
|
if (i & (1 << 1)) {
|
|
|
|
|
wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y', 0, 0);
|
|
|
|
|
wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y');
|
|
|
|
|
}
|
|
|
|
|
if (i & (1 << 2)) {
|
|
|
|
|
wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z', 0, 0);
|
|
|
|
|
wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
copy_v3_v3(cache->true_last_location, cache->true_location);
|
|
|
|
|
cache->is_last_valid = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
|
|
|
|
|
@@ -2160,24 +2842,17 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|
|
|
|
Brush *brush = BKE_paint_brush(&wp->paint);
|
|
|
|
|
struct WPaintData *wpd = paint_stroke_mode_data(stroke);
|
|
|
|
|
ViewContext *vc;
|
|
|
|
|
Object *ob;
|
|
|
|
|
Mesh *me;
|
|
|
|
|
float mat[4][4];
|
|
|
|
|
float paintweight;
|
|
|
|
|
int *indexar;
|
|
|
|
|
unsigned int index, totindex;
|
|
|
|
|
float mval[2];
|
|
|
|
|
const bool use_blur = (brush->vertexpaint_tool == PAINT_BLEND_BLUR);
|
|
|
|
|
bool use_vert_sel;
|
|
|
|
|
bool use_face_sel;
|
|
|
|
|
bool use_depth;
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
|
|
|
|
|
|
|
|
|
vwpaint_update_cache_variants(C, wp, ob, itemptr);
|
|
|
|
|
|
|
|
|
|
float mat[4][4];
|
|
|
|
|
float mval[2];
|
|
|
|
|
|
|
|
|
|
const float pressure = RNA_float_get(itemptr, "pressure");
|
|
|
|
|
const float brush_size_pressure =
|
|
|
|
|
BKE_brush_size_get(scene, brush) * (BKE_brush_use_size_pressure(scene, brush) ? pressure : 1.0f);
|
|
|
|
|
const float brush_alpha_value = BKE_brush_alpha_get(scene, brush);
|
|
|
|
|
const float brush_alpha_pressure =
|
|
|
|
|
brush_alpha_value * (BKE_brush_use_alpha_pressure(scene, brush) ? pressure : 1.0f);
|
|
|
|
|
|
|
|
|
|
/* intentionally don't initialize as NULL, make sure we initialize all members below */
|
|
|
|
|
WeightPaintInfo wpi;
|
|
|
|
|
@@ -2190,13 +2865,8 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float (*blur_weight_func)(const MDeformVert *, const WeightPaintInfo *) =
|
|
|
|
|
wpd->do_multipaint ? wpaint_blur_weight_multi : wpaint_blur_weight_single;
|
|
|
|
|
|
|
|
|
|
vc = &wpd->vc;
|
|
|
|
|
ob = vc->obact;
|
|
|
|
|
me = ob->data;
|
|
|
|
|
indexar = wpd->indexar;
|
|
|
|
|
|
|
|
|
|
view3d_operator_needs_opengl(C);
|
|
|
|
|
ED_view3d_init_mats_rv3d(ob, vc->rv3d);
|
|
|
|
|
@@ -2204,7 +2874,6 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|
|
|
|
/* load projection matrix */
|
|
|
|
|
mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat);
|
|
|
|
|
|
|
|
|
|
RNA_float_get_array(itemptr, "mouse", mval);
|
|
|
|
|
|
|
|
|
|
/* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */
|
|
|
|
|
wpi.defbase_tot = wpd->defbase_tot;
|
|
|
|
|
@@ -2222,159 +2891,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|
|
|
|
wpi.brush_alpha_value = brush_alpha_value;
|
|
|
|
|
/* *** done setting up WeightPaintInfo *** */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
swap_m4m4(wpd->vc.rv3d->persmat, mat);
|
|
|
|
|
|
|
|
|
|
use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
|
|
|
|
use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
use_depth = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0;
|
|
|
|
|
|
|
|
|
|
/* which faces are involved */
|
|
|
|
|
if (use_depth) {
|
|
|
|
|
char editflag_prev = me->editflag;
|
|
|
|
|
|
|
|
|
|
/* Ugly hack, to avoid drawing vertex index when getting the face index buffer - campbell */
|
|
|
|
|
me->editflag &= ~ME_EDIT_PAINT_VERT_SEL;
|
|
|
|
|
if (use_vert_sel) {
|
|
|
|
|
/* Ugly x2, we need this so hidden faces don't draw */
|
|
|
|
|
me->editflag |= ME_EDIT_PAINT_FACE_SEL;
|
|
|
|
|
}
|
|
|
|
|
totindex = sample_backbuf_area(vc, indexar, me->totpoly, mval[0], mval[1], brush_size_pressure);
|
|
|
|
|
me->editflag = editflag_prev;
|
|
|
|
|
|
|
|
|
|
if (use_face_sel && me->totpoly) {
|
|
|
|
|
MPoly *mpoly = me->mpoly;
|
|
|
|
|
for (index = 0; index < totindex; index++) {
|
|
|
|
|
if (indexar[index] && indexar[index] <= me->totpoly) {
|
|
|
|
|
MPoly *mp = &mpoly[indexar[index] - 1];
|
|
|
|
|
|
|
|
|
|
if ((mp->flag & ME_FACE_SEL) == 0) {
|
|
|
|
|
indexar[index] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
indexar = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* incase we have modifiers */
|
|
|
|
|
ED_vpaint_proj_handle_update(wpd->vp_handle, vc->ar, mval);
|
|
|
|
|
|
|
|
|
|
/* make sure each vertex gets treated only once */
|
|
|
|
|
/* and calculate filter weight */
|
|
|
|
|
paintweight = BKE_brush_weight_get(scene, brush);
|
|
|
|
|
|
|
|
|
|
if (use_depth) {
|
|
|
|
|
for (index = 0; index < totindex; index++) {
|
|
|
|
|
if (indexar[index] && indexar[index] <= me->totpoly) {
|
|
|
|
|
MPoly *mpoly = me->mpoly + (indexar[index] - 1);
|
|
|
|
|
MLoop *ml = me->mloop + mpoly->loopstart;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (use_vert_sel) {
|
|
|
|
|
for (i = 0; i < mpoly->totloop; i++, ml++) {
|
|
|
|
|
me->dvert[ml->v].flag = (me->mvert[ml->v].flag & SELECT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (i = 0; i < mpoly->totloop; i++, ml++) {
|
|
|
|
|
me->dvert[ml->v].flag = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const unsigned int totvert = me->totvert;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
/* in the case of face selection we need to flush */
|
|
|
|
|
if (use_vert_sel || use_face_sel) {
|
|
|
|
|
for (i = 0; i < totvert; i++) {
|
|
|
|
|
me->dvert[i].flag = me->mvert[i].flag & SELECT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (i = 0; i < totvert; i++) {
|
|
|
|
|
me->dvert[i].flag = SELECT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* accumulate means we refer to the previous,
|
|
|
|
|
* which is either the last update, or when we started painting */
|
|
|
|
|
BLI_Stack *accumulate_stack = wpd->accumulate_stack;
|
|
|
|
|
const bool use_accumulate = (accumulate_stack != NULL);
|
|
|
|
|
BLI_assert(accumulate_stack == NULL || BLI_stack_is_empty(accumulate_stack));
|
|
|
|
|
|
|
|
|
|
const MDeformVert *dvert_prev = use_accumulate ? me->dvert : wp->wpaint_prev;
|
|
|
|
|
|
|
|
|
|
#define WP_PAINT(v_idx_var) \
|
|
|
|
|
{ \
|
|
|
|
|
unsigned int vidx = v_idx_var; \
|
|
|
|
|
if (me->dvert[vidx].flag) { \
|
|
|
|
|
const float alpha = calc_vp_alpha_col_dl( \
|
|
|
|
|
wp, vc, wpd->wpimat, &wpd->vertexcosnos[vidx], \
|
|
|
|
|
mval, brush_size_pressure, brush_alpha_pressure, NULL); \
|
|
|
|
|
if (alpha) { \
|
|
|
|
|
if (use_blur) { \
|
|
|
|
|
paintweight = wpaint_blur_weight_calc_from_connected( \
|
|
|
|
|
dvert_prev, &wpi, wpd, vidx, blur_weight_func); \
|
|
|
|
|
} \
|
|
|
|
|
if (use_accumulate) { \
|
|
|
|
|
struct WPaintDefer *dweight = BLI_stack_push_r(accumulate_stack); \
|
|
|
|
|
dweight->index = vidx; \
|
|
|
|
|
dweight->alpha = alpha; \
|
|
|
|
|
dweight->weight = paintweight; \
|
|
|
|
|
} \
|
|
|
|
|
else { \
|
|
|
|
|
do_weight_paint_vertex(wp, ob, &wpi, vidx, alpha, paintweight); \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
me->dvert[vidx].flag = 0; \
|
|
|
|
|
} \
|
|
|
|
|
} (void)0
|
|
|
|
|
|
|
|
|
|
if (use_depth) {
|
|
|
|
|
for (index = 0; index < totindex; index++) {
|
|
|
|
|
|
|
|
|
|
if (indexar[index] && indexar[index] <= me->totpoly) {
|
|
|
|
|
MPoly *mpoly = me->mpoly + (indexar[index] - 1);
|
|
|
|
|
MLoop *ml = me->mloop + mpoly->loopstart;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < mpoly->totloop; i++, ml++) {
|
|
|
|
|
WP_PAINT(ml->v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const unsigned int totvert = me->totvert;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < totvert; i++) {
|
|
|
|
|
WP_PAINT(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#undef WP_PAINT
|
|
|
|
|
|
|
|
|
|
if (use_accumulate) {
|
|
|
|
|
unsigned int defer_count = BLI_stack_count(accumulate_stack);
|
|
|
|
|
while (defer_count--) {
|
|
|
|
|
struct WPaintDefer *dweight = BLI_stack_peek(accumulate_stack);
|
|
|
|
|
do_weight_paint_vertex(wp, ob, &wpi, dweight->index, dweight->alpha, dweight->weight);
|
|
|
|
|
BLI_stack_discard(accumulate_stack);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* *** free wpi members */
|
|
|
|
|
/* *** done freeing wpi members */
|
|
|
|
|
|
|
|
|
|
wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi);
|
|
|
|
|
|
|
|
|
|
swap_m4m4(vc->rv3d->persmat, mat);
|
|
|
|
|
|
|
|
|
|
@@ -2383,19 +2900,40 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|
|
|
|
paint_last_stroke_update(scene, vc->ar, mval);
|
|
|
|
|
|
|
|
|
|
DAG_id_tag_update(ob->data, 0);
|
|
|
|
|
ED_region_tag_redraw(vc->ar);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
|
|
|
swap_m4m4(wpd->vc.rv3d->persmat, mat);
|
|
|
|
|
|
|
|
|
|
rcti r;
|
|
|
|
|
if (sculpt_get_redraw_rect(vc->ar, CTX_wm_region_view3d(C), ob, &r)) {
|
|
|
|
|
if (ss->cache) {
|
|
|
|
|
ss->cache->current_r = r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* previous is not set in the current cache else
|
|
|
|
|
* the partial rect will always grow */
|
|
|
|
|
if (ss->cache) {
|
|
|
|
|
if (!BLI_rcti_is_empty(&ss->cache->previous_r))
|
|
|
|
|
BLI_rcti_union(&r, &ss->cache->previous_r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.xmin += vc->ar->winrct.xmin - 2;
|
|
|
|
|
r.xmax += vc->ar->winrct.xmin + 2;
|
|
|
|
|
r.ymin += vc->ar->winrct.ymin - 2;
|
|
|
|
|
r.ymax += vc->ar->winrct.ymin + 2;
|
|
|
|
|
|
|
|
|
|
ss->partial_redraw = 1;
|
|
|
|
|
}
|
|
|
|
|
ED_region_tag_redraw_partial(vc->ar, &r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
|
|
|
|
{
|
|
|
|
|
ToolSettings *ts = CTX_data_tool_settings(C);
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
struct WPaintData *wpd = paint_stroke_mode_data(stroke);
|
|
|
|
|
|
|
|
|
|
if (wpd) {
|
|
|
|
|
ED_vpaint_proj_handle_free(wpd->vp_handle);
|
|
|
|
|
MEM_freeN(wpd->indexar);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (wpd->defbase_sel)
|
|
|
|
|
MEM_freeN((void *)wpd->defbase_sel);
|
|
|
|
|
if (wpd->vgroup_validmap)
|
|
|
|
|
@@ -2407,23 +2945,9 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
|
|
|
|
if (wpd->mirror.lock)
|
|
|
|
|
MEM_freeN((void *)wpd->mirror.lock);
|
|
|
|
|
|
|
|
|
|
if (wpd->blur_data.vmap) {
|
|
|
|
|
MEM_freeN(wpd->blur_data.vmap);
|
|
|
|
|
}
|
|
|
|
|
if (wpd->blur_data.vmap_mem) {
|
|
|
|
|
MEM_freeN(wpd->blur_data.vmap_mem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wpd->accumulate_stack) {
|
|
|
|
|
BLI_stack_free(wpd->accumulate_stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MEM_freeN(wpd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* frees prev buffer */
|
|
|
|
|
copy_wpaint_prev(ts->wpaint, NULL, 0);
|
|
|
|
|
|
|
|
|
|
/* and particles too */
|
|
|
|
|
if (ob->particlesystem.first) {
|
|
|
|
|
ParticleSystem *psys;
|
|
|
|
|
@@ -2442,6 +2966,9 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
|
|
|
|
DAG_id_tag_update(ob->data, 0);
|
|
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
|
|
|
|
|
|
|
|
sculpt_cache_free(ob->sculpt->cache);
|
|
|
|
|
ob->sculpt->cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2449,9 +2976,10 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
op->customdata = paint_stroke_new(C, op, NULL, wpaint_stroke_test_start,
|
|
|
|
|
wpaint_stroke_update_step, NULL,
|
|
|
|
|
wpaint_stroke_done, event->type);
|
|
|
|
|
op->customdata = paint_stroke_new(
|
|
|
|
|
C, op, sculpt_stroke_get_location, wpaint_stroke_test_start,
|
|
|
|
|
wpaint_stroke_update_step, NULL,
|
|
|
|
|
wpaint_stroke_done, event->type);
|
|
|
|
|
|
|
|
|
|
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
|
|
|
|
paint_stroke_data_free(op);
|
|
|
|
|
@@ -2468,9 +2996,10 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
|
|
|
|
|
static int wpaint_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
op->customdata = paint_stroke_new(C, op, NULL, wpaint_stroke_test_start,
|
|
|
|
|
wpaint_stroke_update_step, NULL,
|
|
|
|
|
wpaint_stroke_done, 0);
|
|
|
|
|
op->customdata = paint_stroke_new(
|
|
|
|
|
C, op, sculpt_stroke_get_location, wpaint_stroke_test_start,
|
|
|
|
|
wpaint_stroke_update_step, NULL,
|
|
|
|
|
wpaint_stroke_done, 0);
|
|
|
|
|
|
|
|
|
|
/* frees op->customdata */
|
|
|
|
|
paint_stroke_exec(C, op);
|
|
|
|
|
@@ -2480,6 +3009,12 @@ static int wpaint_exec(bContext *C, wmOperator *op)
|
|
|
|
|
|
|
|
|
|
static void wpaint_cancel(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
if (ob->sculpt->cache) {
|
|
|
|
|
sculpt_cache_free(ob->sculpt->cache);
|
|
|
|
|
ob->sculpt->cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
paint_stroke_cancel(C, op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2516,7 +3051,7 @@ static int weight_paint_set_exec(bContext *C, wmOperator *op)
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ED_wpaint_fill(scene->toolsettings->wpaint, obact, vgroup_weight)) {
|
|
|
|
|
if (ED_wpaint_fill(obact, vgroup_weight)) {
|
|
|
|
|
ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
@@ -2570,6 +3105,14 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|
|
|
|
BKE_mesh_flush_select_from_polys(me);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the cache is not released by a cancel or a done, free it now. */
|
|
|
|
|
if (ob->sculpt->cache) {
|
|
|
|
|
sculpt_cache_free(ob->sculpt->cache);
|
|
|
|
|
ob->sculpt->cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_sculptsession_free(ob);
|
|
|
|
|
|
|
|
|
|
paint_cursor_delete_textures();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
@@ -2585,6 +3128,16 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|
|
|
|
paint_cursor_start(C, vertex_paint_poll);
|
|
|
|
|
|
|
|
|
|
BKE_paint_init(scene, ePaintVertex, PAINT_CURSOR_VERTEX_PAINT);
|
|
|
|
|
|
|
|
|
|
/* Create vertex/weight paint mode session data */
|
|
|
|
|
if (ob->sculpt) {
|
|
|
|
|
if (ob->sculpt->cache) {
|
|
|
|
|
sculpt_cache_free(ob->sculpt->cache);
|
|
|
|
|
ob->sculpt->cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
BKE_sculptsession_free(ob);
|
|
|
|
|
}
|
|
|
|
|
vertex_paint_init_session(scene, ob);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* update modifier stack for mapping requirements */
|
|
|
|
|
@@ -2638,13 +3191,12 @@ typedef struct PolyFaceMap {
|
|
|
|
|
int facenr;
|
|
|
|
|
} PolyFaceMap;
|
|
|
|
|
|
|
|
|
|
typedef struct VPaintData {
|
|
|
|
|
struct VPaintData {
|
|
|
|
|
ViewContext vc;
|
|
|
|
|
unsigned int paintcol;
|
|
|
|
|
int *indexar;
|
|
|
|
|
uint paintcol;
|
|
|
|
|
|
|
|
|
|
struct VertProjHandle *vp_handle;
|
|
|
|
|
DMCoNo *vertexcosnos;
|
|
|
|
|
struct DMCoNo *vertexcosnos;
|
|
|
|
|
|
|
|
|
|
float vpimat[3][3];
|
|
|
|
|
|
|
|
|
|
@@ -2657,9 +3209,9 @@ typedef struct VPaintData {
|
|
|
|
|
bool *mlooptag;
|
|
|
|
|
|
|
|
|
|
bool is_texbrush;
|
|
|
|
|
} VPaintData;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float UNUSED(mouse[2]))
|
|
|
|
|
static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
|
|
|
|
|
{
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
ToolSettings *ts = scene->toolsettings;
|
|
|
|
|
@@ -2670,6 +3222,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
Mesh *me;
|
|
|
|
|
float mat[4][4], imat[4][4];
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
|
|
|
|
|
/* context checks could be a poll() */
|
|
|
|
|
me = BKE_mesh_from_object(ob);
|
|
|
|
|
@@ -2682,13 +3235,10 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* make mode data storage */
|
|
|
|
|
vpd = MEM_callocN(sizeof(struct VPaintData), "VPaintData");
|
|
|
|
|
vpd = MEM_callocN(sizeof(*vpd), "VPaintData");
|
|
|
|
|
paint_stroke_set_mode_data(stroke, vpd);
|
|
|
|
|
view3d_set_viewcontext(C, &vpd->vc);
|
|
|
|
|
|
|
|
|
|
vpd->vp_handle = ED_vpaint_proj_handle_create(vpd->vc.scene, ob, &vpd->vertexcosnos);
|
|
|
|
|
|
|
|
|
|
vpd->indexar = get_indexarray(me);
|
|
|
|
|
vpd->paintcol = vpaint_get_current_col(scene, vp);
|
|
|
|
|
|
|
|
|
|
vpd->is_texbrush = !(brush->vertexpaint_tool == PAINT_BLEND_BLUR) &&
|
|
|
|
|
@@ -2710,84 +3260,535 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
|
|
|
|
|
vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* for filtering */
|
|
|
|
|
copy_vpaint_prev(vp, (unsigned int *)me->mloopcol, me->totloop);
|
|
|
|
|
|
|
|
|
|
/* Create projection handle */
|
|
|
|
|
if (vpd->is_texbrush) {
|
|
|
|
|
ob->sculpt->building_vp_handle = true;
|
|
|
|
|
vpd->vp_handle = ED_vpaint_proj_handle_create(scene, ob, &vpd->vertexcosnos);
|
|
|
|
|
ob->sculpt->building_vp_handle = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* some old cruft to sort out later */
|
|
|
|
|
mul_m4_m4m4(mat, vpd->vc.rv3d->viewmat, ob->obmat);
|
|
|
|
|
invert_m4_m4(imat, mat);
|
|
|
|
|
copy_m3_m4(vpd->vpimat, imat);
|
|
|
|
|
|
|
|
|
|
/* If not previously created, create vertex/weight paint mode session data */
|
|
|
|
|
vertex_paint_init_session(scene, ob);
|
|
|
|
|
vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
|
|
|
|
|
vertex_paint_init_session_data(ts, ob);
|
|
|
|
|
|
|
|
|
|
if (ob->sculpt->mode.vpaint.previous_color != NULL) {
|
|
|
|
|
memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vpaint_paint_poly(VPaint *vp, VPaintData *vpd, Mesh *me,
|
|
|
|
|
const unsigned int index, const float mval[2],
|
|
|
|
|
const float brush_size_pressure, const float brush_alpha_pressure)
|
|
|
|
|
static void do_vpaint_brush_calc_average_color_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
ViewContext *vc = &vpd->vc;
|
|
|
|
|
Brush *brush = BKE_paint_brush(&vp->paint);
|
|
|
|
|
MPoly *mpoly = &me->mpoly[index];
|
|
|
|
|
MLoop *ml;
|
|
|
|
|
unsigned int *lcol = ((unsigned int *)me->mloopcol) + mpoly->loopstart;
|
|
|
|
|
unsigned int *lcolorig = ((unsigned int *)vp->vpaint_prev) + mpoly->loopstart;
|
|
|
|
|
bool *mlooptag = (vpd->mlooptag) ? vpd->mlooptag + mpoly->loopstart : NULL;
|
|
|
|
|
float alpha;
|
|
|
|
|
int i, j;
|
|
|
|
|
int totloop = mpoly->totloop;
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
|
|
|
|
|
|
|
|
|
int brush_alpha_pressure_i = (int)(brush_alpha_pressure * 255.0f);
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
uint *lcol = data->lcol;
|
|
|
|
|
char *col;
|
|
|
|
|
|
|
|
|
|
if (brush->vertexpaint_tool == PAINT_BLEND_BLUR) {
|
|
|
|
|
unsigned int blend[4] = {0};
|
|
|
|
|
unsigned int tcol;
|
|
|
|
|
char *col;
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < totloop; j++) {
|
|
|
|
|
col = (char *)(lcol + j);
|
|
|
|
|
blend[0] += col[0];
|
|
|
|
|
blend[1] += col[1];
|
|
|
|
|
blend[2] += col[2];
|
|
|
|
|
blend[3] += col[3];
|
|
|
|
|
}
|
|
|
|
|
struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n;
|
|
|
|
|
accum->len = 0;
|
|
|
|
|
memset(accum->value, 0, sizeof(accum->value));
|
|
|
|
|
|
|
|
|
|
blend[0] = divide_round_i(blend[0], totloop);
|
|
|
|
|
blend[1] = divide_round_i(blend[1], totloop);
|
|
|
|
|
blend[2] = divide_round_i(blend[2], totloop);
|
|
|
|
|
blend[3] = divide_round_i(blend[3], totloop);
|
|
|
|
|
col = (char *)&tcol;
|
|
|
|
|
col[0] = blend[0];
|
|
|
|
|
col[1] = blend[1];
|
|
|
|
|
col[2] = blend[2];
|
|
|
|
|
col[3] = blend[3];
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
vpd->paintcol = *((unsigned int *)col);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ml = me->mloop + mpoly->loopstart;
|
|
|
|
|
for (i = 0; i < totloop; i++, ml++) {
|
|
|
|
|
float rgba[4];
|
|
|
|
|
unsigned int paintcol;
|
|
|
|
|
alpha = calc_vp_alpha_col_dl(vp, vc, vpd->vpimat,
|
|
|
|
|
&vpd->vertexcosnos[ml->v], mval,
|
|
|
|
|
brush_size_pressure, brush_alpha_pressure, rgba);
|
|
|
|
|
|
|
|
|
|
if (vpd->is_texbrush) {
|
|
|
|
|
float rgba_br[3];
|
|
|
|
|
rgb_uchar_to_float(rgba_br, (const unsigned char *)&vpd->paintcol);
|
|
|
|
|
mul_v3_v3(rgba_br, rgba);
|
|
|
|
|
rgb_float_to_uchar((unsigned char *)&paintcol, rgba_br);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
paintcol = vpd->paintcol;
|
|
|
|
|
|
|
|
|
|
if (alpha > 0.0f) {
|
|
|
|
|
const int alpha_i = (int)(alpha * 255.0f);
|
|
|
|
|
lcol[i] = vpaint_blend(vp, lcol[i], lcolorig[i], paintcol, alpha_i, brush_alpha_pressure_i);
|
|
|
|
|
|
|
|
|
|
if (mlooptag) mlooptag[i] = 1;
|
|
|
|
|
/* For each vertex */
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test_fast(&test, vd.co)) {
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
if (BKE_brush_curve_strength(data->brush, test.dist, cache->radius) > 0.0) {
|
|
|
|
|
/* If the vertex is selected for painting. */
|
|
|
|
|
const MVert *mv = &data->me->mvert[v_index];
|
|
|
|
|
if (!use_face_sel || mv->flag & SELECT) {
|
|
|
|
|
accum->len += gmap->vert_to_loop[v_index].count;
|
|
|
|
|
/* if a vertex is within the brush region, then add it's color to the blend. */
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) {
|
|
|
|
|
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
|
|
|
|
col = (char *)(&lcol[l_index]);
|
|
|
|
|
/* Color is squared to compensate the sqrt color encoding. */
|
|
|
|
|
accum->value[0] += col[0] * col[0];
|
|
|
|
|
accum->value[1] += col[1] * col[1];
|
|
|
|
|
accum->value[2] += col[2] * col[2];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void handle_texture_brush(
|
|
|
|
|
SculptThreadedTaskData *data, PBVHVertexIter vd, float size_pressure, float alpha_pressure,
|
|
|
|
|
float *r_alpha, uint *r_color)
|
|
|
|
|
{
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
|
|
|
|
|
float rgba[4];
|
|
|
|
|
float rgba_br[3];
|
|
|
|
|
|
|
|
|
|
*r_alpha = calc_vp_alpha_col_dl(
|
|
|
|
|
data->vp, &data->vpd->vc, data->vpd->vpimat,
|
|
|
|
|
&data->vpd->vertexcosnos[v_index], ss->cache->mouse, size_pressure, alpha_pressure, rgba);
|
|
|
|
|
rgb_uchar_to_float(rgba_br, (const uchar *)&data->vpd->paintcol);
|
|
|
|
|
mul_v3_v3(rgba_br, rgba);
|
|
|
|
|
rgb_float_to_uchar((uchar *)r_color, rgba_br);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_vpaint_brush_draw_task_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
|
|
|
|
|
|
|
|
|
Brush *brush = data->brush;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
const float brush_strength = cache->bstrength;
|
|
|
|
|
uint *lcol = data->lcol;
|
|
|
|
|
Scene *scene = CTX_data_scene(data->C);
|
|
|
|
|
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
|
|
|
|
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
/* For each vertex*/
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test(&test, vd.co)) {
|
|
|
|
|
/* Note: Grids are 1:1 with corners (aka loops).
|
|
|
|
|
* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
|
|
|
|
|
* Otherwise, take the current vert. */
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
|
|
|
|
|
const MVert *mv = &data->me->mvert[v_index];
|
|
|
|
|
|
|
|
|
|
/* If the vertex is selected for painting. */
|
|
|
|
|
if (!use_face_sel || mv->flag & SELECT) {
|
|
|
|
|
/* Calc the dot prod. between ray norm on surf and current vert
|
|
|
|
|
* (ie splash prevention factor), and only paint front facing verts. */
|
|
|
|
|
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
|
|
|
|
|
if (view_dot > 0.0f) {
|
|
|
|
|
const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
|
|
|
|
|
uint color_final = data->vpd->paintcol;
|
|
|
|
|
|
|
|
|
|
/* If we're painting with a texture, sample the texture color and alpha. */
|
|
|
|
|
float tex_alpha = 1.0;
|
|
|
|
|
if (data->vpd->is_texbrush) {
|
|
|
|
|
handle_texture_brush(
|
|
|
|
|
data, vd, brush_size_pressure, brush_alpha_pressure,
|
|
|
|
|
&tex_alpha, &color_final);
|
|
|
|
|
}
|
|
|
|
|
/* For each poly owning this vert, paint each loop belonging to this vert. */
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
|
|
|
|
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
|
|
|
|
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
|
|
|
|
BLI_assert(data->me->mloop[l_index].v == v_index);
|
|
|
|
|
const MPoly *mp = &data->me->mpoly[p_index];
|
|
|
|
|
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
|
|
|
|
uint color_orig = 0; /* unused when array is NULL */
|
|
|
|
|
if (ss->mode.vpaint.previous_color != NULL) {
|
|
|
|
|
/* Get the previous loop color */
|
|
|
|
|
if (ss->mode.vpaint.previous_color[l_index] == 0) {
|
|
|
|
|
ss->mode.vpaint.previous_color[l_index] = lcol[l_index];
|
|
|
|
|
}
|
|
|
|
|
color_orig = ss->mode.vpaint.previous_color[l_index];
|
|
|
|
|
}
|
|
|
|
|
const float final_alpha =
|
|
|
|
|
255 * brush_fade * brush_strength * view_dot *
|
|
|
|
|
tex_alpha * brush_alpha_pressure * grid_alpha;
|
|
|
|
|
/* Mix the new color with the original based on final_alpha. */
|
|
|
|
|
lcol[l_index] = vpaint_blend(
|
|
|
|
|
data->vp, lcol[l_index], color_orig, color_final,
|
|
|
|
|
final_alpha, 255 * brush_strength);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_vpaint_brush_blur_task_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
|
|
|
|
|
Scene *scene = CTX_data_scene(data->C);
|
|
|
|
|
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
|
|
|
|
Brush *brush = data->brush;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
const float brush_strength = cache->bstrength;
|
|
|
|
|
uint *lcol = data->lcol;
|
|
|
|
|
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
|
|
|
|
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
/* For each vertex */
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test(&test, vd.co)) {
|
|
|
|
|
/* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
|
|
|
|
|
Otherwise, take the current vert. */
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
|
|
|
|
|
const MVert *mv = &data->me->mvert[v_index];
|
|
|
|
|
|
|
|
|
|
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
|
|
|
|
|
if (view_dot > 0.0f) {
|
|
|
|
|
const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
|
|
|
|
|
|
|
|
|
|
/* If the vertex is selected for painting. */
|
|
|
|
|
if (!use_face_sel || mv->flag & SELECT) {
|
|
|
|
|
/* Get the average poly color */
|
|
|
|
|
uint color_final = 0;
|
|
|
|
|
int total_hit_loops = 0;
|
|
|
|
|
uint blend[4] = {0};
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
|
|
|
|
int p_index = gmap->vert_to_poly[v_index].indices[j];
|
|
|
|
|
const MPoly *mp = &data->me->mpoly[p_index];
|
|
|
|
|
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
|
|
|
|
total_hit_loops += mp->totloop;
|
|
|
|
|
for (int k = 0; k < mp->totloop; k++) {
|
|
|
|
|
const uint l_index = mp->loopstart + k;
|
|
|
|
|
const char *col = (const char *)(&lcol[l_index]);
|
|
|
|
|
/* Color is squared to compensate the sqrt color encoding. */
|
|
|
|
|
blend[0] += (uint)col[0] * (uint)col[0];
|
|
|
|
|
blend[1] += (uint)col[1] * (uint)col[1];
|
|
|
|
|
blend[2] += (uint)col[2] * (uint)col[2];
|
|
|
|
|
blend[3] += (uint)col[3] * (uint)col[3];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (total_hit_loops != 0) {
|
|
|
|
|
/* Use rgb^2 color averaging. */
|
|
|
|
|
char *col = (char *)(&color_final);
|
|
|
|
|
col[0] = round_fl_to_uchar(sqrtf(divide_round_i(blend[0], total_hit_loops)));
|
|
|
|
|
col[1] = round_fl_to_uchar(sqrtf(divide_round_i(blend[1], total_hit_loops)));
|
|
|
|
|
col[2] = round_fl_to_uchar(sqrtf(divide_round_i(blend[2], total_hit_loops)));
|
|
|
|
|
col[3] = round_fl_to_uchar(sqrtf(divide_round_i(blend[3], total_hit_loops)));
|
|
|
|
|
|
|
|
|
|
/* For each poly owning this vert, paint each loop belonging to this vert. */
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
|
|
|
|
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
|
|
|
|
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
|
|
|
|
BLI_assert(data->me->mloop[l_index].v == v_index);
|
|
|
|
|
const MPoly *mp = &data->me->mpoly[p_index];
|
|
|
|
|
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
|
|
|
|
uint color_orig = 0; /* unused when array is NULL */
|
|
|
|
|
if (ss->mode.vpaint.previous_color != NULL) {
|
|
|
|
|
/* Get the previous loop color */
|
|
|
|
|
if (ss->mode.vpaint.previous_color[l_index] == 0) {
|
|
|
|
|
ss->mode.vpaint.previous_color[l_index] = lcol[l_index];
|
|
|
|
|
}
|
|
|
|
|
color_orig = ss->mode.vpaint.previous_color[l_index];
|
|
|
|
|
}
|
|
|
|
|
const float final_alpha =
|
|
|
|
|
255 * brush_fade * brush_strength * view_dot *
|
|
|
|
|
brush_alpha_pressure * grid_alpha;
|
|
|
|
|
/* Mix the new color with the original
|
|
|
|
|
* based on the brush strength and the curve. */
|
|
|
|
|
lcol[l_index] = vpaint_blend(
|
|
|
|
|
data->vp, lcol[l_index], color_orig, *((uint *)col),
|
|
|
|
|
final_alpha, 255 * brush_strength);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_vpaint_brush_smear_task_cb_ex(
|
|
|
|
|
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
|
|
|
|
|
{
|
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
|
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
|
|
|
|
|
|
|
|
|
|
Scene *scene = CTX_data_scene(data->C);
|
|
|
|
|
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
|
|
|
|
Brush *brush = data->brush;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
const float brush_strength = cache->bstrength;
|
|
|
|
|
uint *lcol = data->lcol;
|
|
|
|
|
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
|
|
|
|
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
|
|
|
|
float brush_dir[3];
|
|
|
|
|
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
|
|
|
|
|
|
|
|
|
sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
|
|
|
|
|
if (normalize_v3(brush_dir) != 0.0f) {
|
|
|
|
|
|
|
|
|
|
SculptBrushTest test;
|
|
|
|
|
sculpt_brush_test_init(ss, &test);
|
|
|
|
|
|
|
|
|
|
/* For each vertex */
|
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
|
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
|
|
|
|
{
|
|
|
|
|
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
|
|
|
|
if (sculpt_brush_test(&test, vd.co)) {
|
|
|
|
|
/* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
|
|
|
|
|
Otherwise, take the current vert. */
|
|
|
|
|
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
|
|
|
|
|
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
|
|
|
|
|
const MVert *mv_curr = &data->me->mvert[v_index];
|
|
|
|
|
|
|
|
|
|
/* if the vertex is selected for painting. */
|
|
|
|
|
if (!use_face_sel || mv_curr->flag & SELECT) {
|
|
|
|
|
/* Calc the dot prod. between ray norm on surf and current vert
|
|
|
|
|
(ie splash prevention factor), and only paint front facing verts. */
|
|
|
|
|
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
|
|
|
|
|
if (view_dot > 0.0f) {
|
|
|
|
|
const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
|
|
|
|
|
|
|
|
|
|
bool do_color = false;
|
|
|
|
|
/* Minimum dot product between brush direction and current
|
|
|
|
|
* to neighbor direction is 0.0, meaning orthogonal. */
|
|
|
|
|
float stroke_dot_max = 0.0f;
|
|
|
|
|
|
|
|
|
|
/* Get the color of the loop in the opposite direction of the brush movement */
|
|
|
|
|
uint color_final = 0;
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
|
|
|
|
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
|
|
|
|
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
|
|
|
|
BLI_assert(data->me->mloop[l_index].v == v_index);
|
|
|
|
|
const MPoly *mp = &data->me->mpoly[p_index];
|
|
|
|
|
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
|
|
|
|
for (int k = 0; k < mp->totloop; k++) {
|
|
|
|
|
const MLoop *ml = &data->me->mloop[l_index];
|
|
|
|
|
const uint v_other_index = ml->v;
|
|
|
|
|
const MVert *mv_other = &data->me->mvert[v_other_index];
|
|
|
|
|
|
|
|
|
|
/* Get the direction from the selected vert to the neighbor. */
|
|
|
|
|
float other_dir[3];
|
|
|
|
|
sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co);
|
|
|
|
|
normalize_v3(other_dir);
|
|
|
|
|
|
|
|
|
|
const float stroke_dot = dot_v3v3(other_dir, brush_dir);
|
|
|
|
|
|
|
|
|
|
if (stroke_dot > stroke_dot_max) {
|
|
|
|
|
stroke_dot_max = stroke_dot;
|
|
|
|
|
color_final = lcol[l_index];
|
|
|
|
|
do_color = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (do_color) {
|
|
|
|
|
/* For each poly owning this vert, paint each loop belonging to this vert. */
|
|
|
|
|
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
|
|
|
|
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
|
|
|
|
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
|
|
|
|
BLI_assert(data->me->mloop[l_index].v == v_index);
|
|
|
|
|
const MPoly *mp = &data->me->mpoly[p_index];
|
|
|
|
|
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
|
|
|
|
/* Get the previous loop color */
|
|
|
|
|
uint color_orig = 0; /* unused when array is NULL */
|
|
|
|
|
if (ss->mode.vpaint.previous_color != NULL) {
|
|
|
|
|
/* Get the previous loop color */
|
|
|
|
|
if (ss->mode.vpaint.previous_color[l_index] == 0) {
|
|
|
|
|
ss->mode.vpaint.previous_color[l_index] = lcol[l_index];
|
|
|
|
|
}
|
|
|
|
|
color_orig = ss->mode.vpaint.previous_color[l_index];
|
|
|
|
|
}
|
|
|
|
|
const float final_alpha =
|
|
|
|
|
255 * brush_fade * brush_strength *
|
|
|
|
|
view_dot * brush_alpha_pressure * grid_alpha;
|
|
|
|
|
/* Mix the new color with the original
|
|
|
|
|
* based on the brush strength and the curve. */
|
|
|
|
|
lcol[l_index] = vpaint_blend(
|
|
|
|
|
data->vp, lcol[l_index], color_orig, color_final,
|
|
|
|
|
final_alpha, 255 * brush_strength);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void calculate_average_color(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode)
|
|
|
|
|
{
|
|
|
|
|
struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__);
|
|
|
|
|
data->custom_data = accum;
|
|
|
|
|
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, data, NULL, 0, do_vpaint_brush_calc_average_color_cb_ex,
|
|
|
|
|
true, false);
|
|
|
|
|
|
|
|
|
|
uint accum_len = 0;
|
|
|
|
|
uint accum_value[3] = {0};
|
|
|
|
|
uchar blend[4] = {0};
|
|
|
|
|
for (int i = 0; i < totnode; i++) {
|
|
|
|
|
accum_len += accum[i].len;
|
|
|
|
|
accum_value[0] += accum[i].value[0];
|
|
|
|
|
accum_value[1] += accum[i].value[1];
|
|
|
|
|
accum_value[2] += accum[i].value[2];
|
|
|
|
|
}
|
|
|
|
|
if (accum_len != 0) {
|
|
|
|
|
blend[0] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[0], accum_len)));
|
|
|
|
|
blend[1] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[1], accum_len)));
|
|
|
|
|
blend[2] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[2], accum_len)));
|
|
|
|
|
blend[3] = 255;
|
|
|
|
|
data->vpd->paintcol = *((uint *)blend);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(data->custom_data); /* 'accum' */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vpaint_paint_leaves(
|
|
|
|
|
bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd,
|
|
|
|
|
Object *ob, Mesh *me, PBVHNode **nodes, int totnode)
|
|
|
|
|
{
|
|
|
|
|
Brush *brush = ob->sculpt->cache->brush;
|
|
|
|
|
|
|
|
|
|
SculptThreadedTaskData data = {
|
|
|
|
|
.sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .vp = vp, .vpd = vpd,
|
|
|
|
|
.lcol = (uint *)me->mloopcol, .me = me, .C = C,
|
|
|
|
|
};
|
|
|
|
|
switch (brush->vertexpaint_tool) {
|
|
|
|
|
case PAINT_BLEND_AVERAGE:
|
|
|
|
|
calculate_average_color(&data, nodes, totnode);
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_vpaint_brush_draw_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
case PAINT_BLEND_BLUR:
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_vpaint_brush_blur_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
case PAINT_BLEND_SMEAR:
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_vpaint_brush_smear_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
BLI_task_parallel_range_ex(
|
|
|
|
|
0, totnode, &data, NULL, 0,
|
|
|
|
|
do_vpaint_brush_draw_task_cb_ex, true, false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vpaint_do_paint(
|
|
|
|
|
bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd,
|
|
|
|
|
Object *ob, Mesh *me, Brush *UNUSED(brush), const char symm, const int axis, const int i, const float angle)
|
|
|
|
|
{
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
ss->cache->radial_symmetry_pass = i;
|
|
|
|
|
calc_brushdata_symm(vd, ss->cache, symm, axis, angle);
|
|
|
|
|
SculptSearchSphereData data;
|
|
|
|
|
PBVHNode **nodes = NULL;
|
|
|
|
|
int totnode;
|
|
|
|
|
|
|
|
|
|
/* Build a list of all nodes that are potentially within the brush's area of influence */
|
|
|
|
|
data.ss = ss;
|
|
|
|
|
data.sd = sd;
|
|
|
|
|
data.radius_squared = ss->cache->radius_squared;
|
|
|
|
|
data.original = true;
|
|
|
|
|
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
|
|
|
|
|
|
|
|
|
|
calc_area_normal(vd, ob, nodes, totnode, ss->cache->sculpt_normal_symm);
|
|
|
|
|
|
|
|
|
|
/* Paint those leaves. */
|
|
|
|
|
vpaint_paint_leaves(C, sd, vd, vpd, ob, me, nodes, totnode);
|
|
|
|
|
|
|
|
|
|
if (nodes) {
|
|
|
|
|
MEM_freeN(nodes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vpaint_do_radial_symmetry(
|
|
|
|
|
bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob, Mesh *me,
|
|
|
|
|
Brush *brush, const char symm, const int axis)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 1; i < vd->radial_symm[axis - 'X']; i++) {
|
|
|
|
|
const float angle = (2.0 * M_PI) * i / vd->radial_symm[axis - 'X'];
|
|
|
|
|
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, symm, axis, i, angle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vpaint_do_symmetrical_brush_actions(
|
|
|
|
|
bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob)
|
|
|
|
|
{
|
|
|
|
|
Brush *brush = BKE_paint_brush(&vd->paint);
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
|
StrokeCache *cache = ss->cache;
|
|
|
|
|
const char symm = vd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
/* initial stroke */
|
|
|
|
|
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0);
|
|
|
|
|
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X');
|
|
|
|
|
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y');
|
|
|
|
|
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z');
|
|
|
|
|
|
|
|
|
|
cache->symmetry = symm;
|
|
|
|
|
|
|
|
|
|
/* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
|
|
|
|
|
for (i = 1; i <= symm; i++) {
|
|
|
|
|
if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) {
|
|
|
|
|
cache->mirror_symmetry_pass = i;
|
|
|
|
|
cache->radial_symmetry_pass = 0;
|
|
|
|
|
calc_brushdata_symm(vd, cache, i, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (i & (1 << 0)) {
|
|
|
|
|
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0);
|
|
|
|
|
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X');
|
|
|
|
|
}
|
|
|
|
|
if (i & (1 << 1)) {
|
|
|
|
|
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Y', 0, 0);
|
|
|
|
|
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y');
|
|
|
|
|
}
|
|
|
|
|
if (i & (1 << 2)) {
|
|
|
|
|
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Z', 0, 0);
|
|
|
|
|
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy_v3_v3(cache->true_last_location, cache->true_location);
|
|
|
|
|
cache->is_last_valid = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
|
|
|
|
|
@@ -2796,65 +3797,26 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|
|
|
|
ToolSettings *ts = CTX_data_tool_settings(C);
|
|
|
|
|
struct VPaintData *vpd = paint_stroke_mode_data(stroke);
|
|
|
|
|
VPaint *vp = ts->vpaint;
|
|
|
|
|
Brush *brush = BKE_paint_brush(&vp->paint);
|
|
|
|
|
ViewContext *vc = &vpd->vc;
|
|
|
|
|
Object *ob = vc->obact;
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
|
|
|
|
|
|
|
|
|
vwpaint_update_cache_variants(C, vp, ob, itemptr);
|
|
|
|
|
|
|
|
|
|
float mat[4][4];
|
|
|
|
|
int *indexar = vpd->indexar;
|
|
|
|
|
int totindex, index;
|
|
|
|
|
float mval[2];
|
|
|
|
|
|
|
|
|
|
const float pressure = RNA_float_get(itemptr, "pressure");
|
|
|
|
|
const float brush_size_pressure =
|
|
|
|
|
BKE_brush_size_get(scene, brush) * (BKE_brush_use_size_pressure(scene, brush) ? pressure : 1.0f);
|
|
|
|
|
const float brush_alpha_pressure =
|
|
|
|
|
BKE_brush_alpha_get(scene, brush) * (BKE_brush_use_alpha_pressure(scene, brush) ? pressure : 1.0f);
|
|
|
|
|
|
|
|
|
|
RNA_float_get_array(itemptr, "mouse", mval);
|
|
|
|
|
|
|
|
|
|
view3d_operator_needs_opengl(C);
|
|
|
|
|
ED_view3d_init_mats_rv3d(ob, vc->rv3d);
|
|
|
|
|
|
|
|
|
|
/* load projection matrix */
|
|
|
|
|
mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat);
|
|
|
|
|
|
|
|
|
|
/* which faces are involved */
|
|
|
|
|
totindex = sample_backbuf_area(vc, indexar, me->totpoly, mval[0], mval[1], brush_size_pressure);
|
|
|
|
|
|
|
|
|
|
if ((me->editflag & ME_EDIT_PAINT_FACE_SEL) && me->mpoly) {
|
|
|
|
|
for (index = 0; index < totindex; index++) {
|
|
|
|
|
if (indexar[index] && indexar[index] <= me->totpoly) {
|
|
|
|
|
const MPoly *mpoly = &me->mpoly[indexar[index] - 1];
|
|
|
|
|
|
|
|
|
|
if ((mpoly->flag & ME_FACE_SEL) == 0)
|
|
|
|
|
indexar[index] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
swap_m4m4(vc->rv3d->persmat, mat);
|
|
|
|
|
|
|
|
|
|
/* incase we have modifiers */
|
|
|
|
|
ED_vpaint_proj_handle_update(vpd->vp_handle, vc->ar, mval);
|
|
|
|
|
vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob);
|
|
|
|
|
|
|
|
|
|
/* clear modified tag for blur tool */
|
|
|
|
|
if (vpd->mlooptag)
|
|
|
|
|
memset(vpd->mlooptag, 0, sizeof(bool) * me->totloop);
|
|
|
|
|
|
|
|
|
|
for (index = 0; index < totindex; index++) {
|
|
|
|
|
if (indexar[index] && indexar[index] <= me->totpoly) {
|
|
|
|
|
vpaint_paint_poly(vp, vpd, me, indexar[index] - 1, mval, brush_size_pressure, brush_alpha_pressure);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
swap_m4m4(vc->rv3d->persmat, mat);
|
|
|
|
|
|
|
|
|
|
/* was disabled because it is slow, but necessary for blur */
|
|
|
|
|
if (brush->vertexpaint_tool == PAINT_BLEND_BLUR) {
|
|
|
|
|
do_shared_vertexcol(me, vpd->mlooptag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* calculate pivot for rotation around seletion if needed */
|
|
|
|
|
/* also needed for "View Selected" on last stroke */
|
|
|
|
|
paint_last_stroke_update(scene, vc->ar, mval);
|
|
|
|
|
@@ -2874,32 +3836,26 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|
|
|
|
|
|
|
|
|
static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
|
|
|
|
{
|
|
|
|
|
ToolSettings *ts = CTX_data_tool_settings(C);
|
|
|
|
|
struct VPaintData *vpd = paint_stroke_mode_data(stroke);
|
|
|
|
|
ViewContext *vc = &vpd->vc;
|
|
|
|
|
Object *ob = vc->obact;
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
|
|
|
|
|
ED_vpaint_proj_handle_free(vpd->vp_handle);
|
|
|
|
|
MEM_freeN(vpd->indexar);
|
|
|
|
|
|
|
|
|
|
/* frees prev buffer */
|
|
|
|
|
copy_vpaint_prev(ts->vpaint, NULL, 0);
|
|
|
|
|
|
|
|
|
|
if (vpd->mlooptag)
|
|
|
|
|
MEM_freeN(vpd->mlooptag);
|
|
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
|
|
|
DAG_id_tag_update(&me->id, 0);
|
|
|
|
|
|
|
|
|
|
MEM_freeN(vpd);
|
|
|
|
|
|
|
|
|
|
sculpt_cache_free(ob->sculpt->cache);
|
|
|
|
|
ob->sculpt->cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
op->customdata = paint_stroke_new(C, op, NULL, vpaint_stroke_test_start,
|
|
|
|
|
op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, vpaint_stroke_test_start,
|
|
|
|
|
vpaint_stroke_update_step, NULL,
|
|
|
|
|
vpaint_stroke_done, event->type);
|
|
|
|
|
|
|
|
|
|
@@ -2919,7 +3875,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
|
|
|
|
|
static int vpaint_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
op->customdata = paint_stroke_new(C, op, NULL, vpaint_stroke_test_start,
|
|
|
|
|
op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, vpaint_stroke_test_start,
|
|
|
|
|
vpaint_stroke_update_step, NULL,
|
|
|
|
|
vpaint_stroke_done, 0);
|
|
|
|
|
|
|
|
|
|
@@ -2931,6 +3887,12 @@ static int vpaint_exec(bContext *C, wmOperator *op)
|
|
|
|
|
|
|
|
|
|
static void vpaint_cancel(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
if (ob->sculpt->cache) {
|
|
|
|
|
sculpt_cache_free(ob->sculpt->cache);
|
|
|
|
|
ob->sculpt->cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
paint_stroke_cancel(C, op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -3014,6 +3976,11 @@ typedef struct DMGradient_vertStore {
|
|
|
|
|
} flag;
|
|
|
|
|
} DMGradient_vertStore;
|
|
|
|
|
|
|
|
|
|
typedef struct DMGradient_vertStoreBase {
|
|
|
|
|
struct WPaintPrev wpp;
|
|
|
|
|
DMGradient_vertStore elem[0];
|
|
|
|
|
} DMGradient_vertStoreBase;
|
|
|
|
|
|
|
|
|
|
typedef struct DMGradient_userData {
|
|
|
|
|
struct ARegion *ar;
|
|
|
|
|
Scene *scene;
|
|
|
|
|
@@ -3024,7 +3991,7 @@ typedef struct DMGradient_userData {
|
|
|
|
|
float sco_line_div; /* store (1.0f / len_v2v2(sco_start, sco_end)) */
|
|
|
|
|
int def_nr;
|
|
|
|
|
bool is_init;
|
|
|
|
|
DMGradient_vertStore *vert_cache;
|
|
|
|
|
DMGradient_vertStoreBase *vert_cache;
|
|
|
|
|
/* only for init */
|
|
|
|
|
BLI_bitmap *vert_visit;
|
|
|
|
|
|
|
|
|
|
@@ -3037,7 +4004,7 @@ typedef struct DMGradient_userData {
|
|
|
|
|
static void gradientVert_update(DMGradient_userData *grad_data, int index)
|
|
|
|
|
{
|
|
|
|
|
Mesh *me = grad_data->me;
|
|
|
|
|
DMGradient_vertStore *vs = &grad_data->vert_cache[index];
|
|
|
|
|
DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
|
|
|
|
|
float alpha;
|
|
|
|
|
|
|
|
|
|
if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
|
|
|
|
|
@@ -3088,7 +4055,7 @@ static void gradientVertUpdate__mapFunc(
|
|
|
|
|
DMGradient_userData *grad_data = userData;
|
|
|
|
|
Mesh *me = grad_data->me;
|
|
|
|
|
if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
|
|
|
|
|
DMGradient_vertStore *vs = &grad_data->vert_cache[index];
|
|
|
|
|
DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
|
|
|
|
|
if (vs->sco[0] != FLT_MAX) {
|
|
|
|
|
gradientVert_update(grad_data, index);
|
|
|
|
|
}
|
|
|
|
|
@@ -3108,14 +4075,14 @@ static void gradientVertInit__mapFunc(
|
|
|
|
|
* updating the mesh may move them about (entering feedback loop) */
|
|
|
|
|
|
|
|
|
|
if (BLI_BITMAP_TEST(grad_data->vert_visit, index) == 0) {
|
|
|
|
|
DMGradient_vertStore *vs = &grad_data->vert_cache[index];
|
|
|
|
|
DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
|
|
|
|
|
if (ED_view3d_project_float_object(grad_data->ar,
|
|
|
|
|
co, vs->sco,
|
|
|
|
|
V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK)
|
|
|
|
|
{
|
|
|
|
|
/* ok */
|
|
|
|
|
MDeformVert *dv = &me->dvert[index];
|
|
|
|
|
MDeformWeight *dw;
|
|
|
|
|
const MDeformWeight *dw;
|
|
|
|
|
dw = defvert_find_index(dv, grad_data->def_nr);
|
|
|
|
|
if (dw) {
|
|
|
|
|
vs->weight_orig = dw->weight;
|
|
|
|
|
@@ -3141,34 +4108,37 @@ static void gradientVertInit__mapFunc(
|
|
|
|
|
static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
int ret = WM_gesture_straightline_modal(C, op, event);
|
|
|
|
|
wmGesture *gesture = op->customdata;
|
|
|
|
|
DMGradient_vertStoreBase *vert_cache = gesture->userdata;
|
|
|
|
|
bool do_gesture_free = false;
|
|
|
|
|
|
|
|
|
|
if (ret & OPERATOR_RUNNING_MODAL) {
|
|
|
|
|
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { /* XXX, hardcoded */
|
|
|
|
|
/* generally crap! redo! */
|
|
|
|
|
WM_gesture_straightline_cancel(C, op);
|
|
|
|
|
do_gesture_free = true;
|
|
|
|
|
ret &= ~OPERATOR_RUNNING_MODAL;
|
|
|
|
|
ret |= OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret & OPERATOR_CANCELLED) {
|
|
|
|
|
ToolSettings *ts = CTX_data_tool_settings(C);
|
|
|
|
|
VPaint *wp = ts->wpaint;
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
Mesh *me = ob->data;
|
|
|
|
|
if (wp->wpaint_prev) {
|
|
|
|
|
if (vert_cache->wpp.wpaint_prev) {
|
|
|
|
|
BKE_defvert_array_free_elems(me->dvert, me->totvert);
|
|
|
|
|
BKE_defvert_array_copy(me->dvert, wp->wpaint_prev, me->totvert);
|
|
|
|
|
free_wpaint_prev(wp);
|
|
|
|
|
BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
|
|
|
|
|
wpaint_prev_destroy(&vert_cache->wpp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
|
|
|
|
}
|
|
|
|
|
else if (ret & OPERATOR_FINISHED) {
|
|
|
|
|
ToolSettings *ts = CTX_data_tool_settings(C);
|
|
|
|
|
VPaint *wp = ts->wpaint;
|
|
|
|
|
free_wpaint_prev(wp);
|
|
|
|
|
wpaint_prev_destroy(&vert_cache->wpp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (do_gesture_free) {
|
|
|
|
|
WM_gesture_straightline_cancel(C, op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
@@ -3177,7 +4147,7 @@ static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEven
|
|
|
|
|
static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
wmGesture *gesture = op->customdata;
|
|
|
|
|
DMGradient_vertStore *vert_cache;
|
|
|
|
|
DMGradient_vertStoreBase *vert_cache;
|
|
|
|
|
struct ARegion *ar = CTX_wm_region(C);
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
@@ -3195,12 +4165,13 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
|
|
|
|
|
|
|
|
|
|
if (is_interactive) {
|
|
|
|
|
if (gesture->userdata == NULL) {
|
|
|
|
|
VPaint *wp = scene->toolsettings->wpaint;
|
|
|
|
|
|
|
|
|
|
gesture->userdata = MEM_mallocN(sizeof(DMGradient_vertStore) * me->totvert, __func__);
|
|
|
|
|
gesture->userdata = MEM_mallocN(
|
|
|
|
|
sizeof(DMGradient_vertStoreBase) +
|
|
|
|
|
(sizeof(DMGradient_vertStore) * me->totvert),
|
|
|
|
|
__func__);
|
|
|
|
|
data.is_init = true;
|
|
|
|
|
|
|
|
|
|
copy_wpaint_prev(wp, me->dvert, me->totvert);
|
|
|
|
|
wpaint_prev_create(&((DMGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert);
|
|
|
|
|
|
|
|
|
|
/* on init only, convert face -> vert sel */
|
|
|
|
|
if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
|
|
|
|
|
@@ -3216,7 +4187,10 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data.is_init = true;
|
|
|
|
|
vert_cache = MEM_mallocN(sizeof(DMGradient_vertStore) * me->totvert, __func__);
|
|
|
|
|
vert_cache = MEM_mallocN(
|
|
|
|
|
sizeof(DMGradient_vertStoreBase) +
|
|
|
|
|
(sizeof(DMGradient_vertStore) * me->totvert),
|
|
|
|
|
__func__);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data.ar = ar;
|
|
|
|
|
|