Cleanup: split out tools & utils from vertex paint

paint_vertex.c was getting too big, move all code unrelated to
mode switching and modal painting into their own files.

Also replace vertex-color operators region redraw tag /w notifiers.
This commit is contained in:
Campbell Barton 2017-09-29 20:03:58 +10:00
parent 5a1954a5cb
commit 5553037be7
Notes: blender-bot 2023-08-14 08:51:59 +02:00
Referenced by issue #111007, Weight Paint gradient operators wait for mouse input
9 changed files with 2245 additions and 2029 deletions

View File

@ -53,6 +53,9 @@ set(SRC
paint_utils.c
paint_vertex.c
paint_vertex_color_ops.c
paint_vertex_color_utils.c
paint_vertex_weight_ops.c
paint_vertex_weight_utils.c
paint_vertex_proj.c
sculpt.c
sculpt_undo.c

View File

@ -96,21 +96,11 @@ int weight_paint_mode_poll(struct bContext *C);
int vertex_paint_poll(struct bContext *C);
int vertex_paint_mode_poll(struct bContext *C);
bool ED_vpaint_fill(struct Object *ob, unsigned int paintcol);
bool ED_wpaint_fill(struct Object *ob, float paintweight);
bool ED_vpaint_smooth(struct Object *ob);
typedef void (*VPaintTransform_Callback)(const float col[3], const void *user_data, float r_col[3]);
bool ED_vpaint_color_transform(struct Object *ob, VPaintTransform_Callback vpaint_tx_fn, const void *user_data);
void PAINT_OT_weight_paint_toggle(struct wmOperatorType *ot);
void PAINT_OT_weight_paint(struct wmOperatorType *ot);
void PAINT_OT_weight_set(struct wmOperatorType *ot);
void PAINT_OT_weight_from_bones(struct wmOperatorType *ot);
void PAINT_OT_weight_sample(struct wmOperatorType *ot);
void PAINT_OT_weight_sample_group(struct wmOperatorType *ot);
enum {
WPAINT_GRADIENT_TYPE_LINEAR,
@ -123,8 +113,44 @@ void PAINT_OT_vertex_paint(struct wmOperatorType *ot);
unsigned int vpaint_get_current_col(struct Scene *scene, struct VPaint *vp);
/* paint_vertex_color_utils.c */
unsigned int ED_vpaint_blend_tool(
const int tool, const uint col,
const uint paintcol, const int alpha_i);
bool ED_vpaint_color_transform(
struct Object *ob, VPaintTransform_Callback vpaint_tx_fn, const void *user_data);
/* paint_vertex_weight_utils.c */
float ED_wpaint_blend_tool(
const int tool,
const float weight,
const float paintval, const float alpha);
/* Utility for tools to ensure vertex groups exist before they begin. */
enum eWPaintFlag {
WPAINT_ENSURE_MIRROR = (1 << 0),
};
struct WPaintVGroupIndex {
int active;
int mirror;
};
bool ED_wpaint_ensure_data(
struct bContext *C, struct ReportList *reports,
enum eWPaintFlag flag, struct WPaintVGroupIndex *vgroup_index);
int ED_wpaint_mirror_vgroup_ensure(struct Object *ob, const int vgroup_active);
/* paint_vertex_color_ops.c */
void PAINT_OT_vertex_color_set(struct wmOperatorType *ot);
void PAINT_OT_vertex_color_from_weight(struct wmOperatorType *ot);
void PAINT_OT_vertex_color_smooth(struct wmOperatorType *ot);
void PAINT_OT_vertex_color_brightness_contrast(struct wmOperatorType *ot);
void PAINT_OT_vertex_color_hsv(struct wmOperatorType *ot);
void PAINT_OT_vertex_color_invert(struct wmOperatorType *ot);
void PAINT_OT_vertex_color_levels(struct wmOperatorType *ot);
/* paint_vertex_weight_ops.c */
void PAINT_OT_weight_from_bones(struct wmOperatorType *ot);
void PAINT_OT_weight_sample(struct wmOperatorType *ot);
void PAINT_OT_weight_sample_group(struct wmOperatorType *ot);
/* paint_vertex_proj.c */
struct VertProjHandle;
@ -162,7 +188,7 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr);
void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th);
int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy);
void *paint_2d_new_stroke(struct bContext *, struct wmOperator *, int mode);
void paint_2d_redraw(const bContext *C, void *ps, bool final);
void paint_2d_redraw(const struct bContext *C, void *ps, bool final);
void paint_2d_stroke_done(void *ps);
void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size);
void paint_2d_bucket_fill(const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps);
@ -219,7 +245,7 @@ float paint_calc_object_space_radius(struct ViewContext *vc, const float center[
float paint_get_tex_pixel(struct MTex *mtex, float u, float v, struct ImagePool *pool, int thread);
void paint_get_tex_pixel_col(struct MTex *mtex, float u, float v, float rgba[4], struct ImagePool *pool, int thread, bool convert, struct ColorSpace *colorspace);
void paint_sample_color(bContext *C, struct ARegion *ar, int x, int y, bool texpaint_proj, bool palette);
void paint_sample_color(struct bContext *C, struct ARegion *ar, int x, int y, bool texpaint_proj, bool palette);
void paint_stroke_operator_properties(struct wmOperatorType *ot);

View File

@ -256,300 +256,6 @@ static void PALETTE_OT_color_delete(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Object *obact = CTX_data_active_object(C);
unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint);
if (ED_vpaint_fill(obact, paintcol)) {
ED_region_tag_redraw(CTX_wm_region(C)); // XXX - should redraw all 3D views
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Vertex Colors";
ot->idname = "PAINT_OT_vertex_color_set";
ot->description = "Fill the active vertex color layer with the current paint color";
/* api callbacks */
ot->exec = vertex_color_set_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obact = CTX_data_active_object(C);
if (ED_vpaint_smooth(obact)) {
ED_region_tag_redraw(CTX_wm_region(C)); // XXX - should redraw all 3D views
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Smooth Vertex Colors";
ot->idname = "PAINT_OT_vertex_color_smooth";
ot->description = "Smooth colors across vertices";
/* api callbacks */
ot->exec = vertex_color_smooth_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \name Vertex Color Transformations
* \{ */
struct VPaintTx_BrightContrastData {
/* pre-calculated */
float gain;
float offset;
};
static void vpaint_tx_brightness_contrast(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_BrightContrastData *data = user_data;
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * col[i] + data->offset;
}
}
static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
float gain, offset;
{
float brightness = RNA_float_get(op->ptr, "brightness");
float contrast = RNA_float_get(op->ptr, "contrast");
brightness /= 100.0f;
float delta = contrast / 200.0f;
gain = 1.0f - delta * 2.0f;
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
* Extracted of OpenCV demhist.c
*/
if (contrast > 0) {
gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
offset = gain * (brightness - delta);
}
else {
delta *= -1;
offset = gain * (brightness + delta);
}
}
const struct VPaintTx_BrightContrastData user_data = {
.gain = gain,
.offset = offset,
};
if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Vertex Paint Bright/Contrast";
ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
ot->description = "Adjust vertex color brightness/contrast";
/* api callbacks */
ot->exec = vertex_color_brightness_contrast_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
const float min = -100, max = +100;
prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
RNA_def_property_ui_range(prop, min, max, 1, 1);
}
struct VPaintTx_HueSatData {
float hue;
float sat;
float val;
};
static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_HueSatData *data = user_data;
float hsv[3];
rgb_to_hsv_v(col, hsv);
hsv[0] += (data->hue - 0.5f);
if (hsv[0] > 1.0f) {
hsv[0] -= 1.0f;
}
else if (hsv[0] < 0.0f) {
hsv[0] += 1.0f;
}
hsv[1] *= data->sat;
hsv[2] *= data->val;
hsv_to_rgb_v(hsv, r_col);
}
static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
const struct VPaintTx_HueSatData user_data = {
.hue = RNA_float_get(op->ptr, "h"),
.sat = RNA_float_get(op->ptr, "s"),
.val = RNA_float_get(op->ptr, "v"),
};
if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Hue Saturation Value";
ot->idname = "PAINT_OT_vertex_color_hsv";
ot->description = "Adjust vertex color HSV values";
/* api callbacks */
ot->exec = vertex_color_hsv_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
}
static void vpaint_tx_invert(const float col[3], const void *UNUSED(user_data), float r_col[3])
{
for (int i = 0; i < 3; i++) {
r_col[i] = 1.0f - col[i];
}
}
static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obact = CTX_data_active_object(C);
if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Invert";
ot->idname = "PAINT_OT_vertex_color_invert";
ot->description = "Invert RGB values";
/* api callbacks */
ot->exec = vertex_color_invert_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
struct VPaintTx_LevelsData {
float gain;
float offset;
};
static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_LevelsData *data = user_data;
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * (col[i] + data->offset);
}
}
static int vertex_color_levels_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
const struct VPaintTx_LevelsData user_data = {
.gain = RNA_float_get(op->ptr, "gain"),
.offset = RNA_float_get(op->ptr, "offset"),
};
if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) {
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
static void PAINT_OT_vertex_color_levels(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Levels";
ot->idname = "PAINT_OT_vertex_color_levels";
ot->description = "Adjust levels of vertex colors";
/* api callbacks */
ot->exec = vertex_color_levels_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
RNA_def_float(ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
RNA_def_float(ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
}
/** \} */
static int brush_reset_exec(bContext *C, wmOperator *UNUSED(op))
{
Paint *paint = BKE_paint_get_active_from_context(C);

File diff suppressed because it is too large Load Diff

View File

@ -27,12 +27,19 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_math_base.h"
#include "BLI_math_color.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_mesh.h"
#include "BKE_deform.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "WM_types.h"
@ -49,9 +56,90 @@ static int vertex_weight_paint_mode_poll(bContext *C)
(me && me->totpoly && me->dvert);
}
static bool vertex_paint_from_weight(bContext *C)
/* -------------------------------------------------------------------- */
/** \name Set Vertex Colors Operator
* \{ */
static bool vertex_color_set(Object *ob, uint paintcol)
{
Mesh *me;
const MPoly *mp;
int i, j;
if (((me = BKE_mesh_from_object(ob)) == NULL) ||
(ED_mesh_color_ensure(me, NULL) == false))
{
return false;
}
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
mp = me->mpoly;
for (i = 0; i < me->totpoly; i++, mp++) {
MLoopCol *lcol = me->mloopcol + mp->loopstart;
if (use_face_sel && !(mp->flag & ME_FACE_SEL))
continue;
j = 0;
do {
uint vidx = me->mloop[mp->loopstart + j].v;
if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) {
*(int *)lcol = paintcol;
}
lcol++;
j++;
} while (j < mp->totloop);
}
/* remove stale me->mcol, will be added later */
BKE_mesh_tessface_clear(me);
DAG_id_tag_update(&me->id, 0);
return true;
}
static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Object *obact = CTX_data_active_object(C);
unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint);
if (vertex_color_set(obact, paintcol)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_vertex_color_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Vertex Colors";
ot->idname = "PAINT_OT_vertex_color_set";
ot->description = "Fill the active vertex color layer with the current paint color";
/* api callbacks */
ot->exec = vertex_color_set_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Vertex Color from Weight Operator
* \{ */
static bool vertex_paint_from_weight(Object *ob)
{
Object *ob = CTX_data_active_object(C);
Mesh *me;
const MPoly *mp;
int vgroup_active;
@ -68,7 +156,7 @@ static bool vertex_paint_from_weight(bContext *C)
for (int i = 0; i < me->totpoly; i++, mp++) {
MLoopCol *lcol = &me->mloopcol[mp->loopstart];
uint j = 0;
do{
do {
uint vidx = me->mloop[mp->loopstart + j].v;
const float weight = defvert_find_weight(&me->dvert[vidx], vgroup_active);
const uchar grayscale = weight * 255;
@ -81,16 +169,20 @@ static bool vertex_paint_from_weight(bContext *C)
}
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
return true;
}
static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op))
{
if (vertex_paint_from_weight(C)) {
Object *obact = CTX_data_active_object(C);
if (vertex_paint_from_weight(obact)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot)
@ -109,3 +201,374 @@ void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot)
/* TODO: invert, alpha */
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Smooth Vertex Colors Operator
* \{ */
static void vertex_color_smooth_looptag(Mesh *me, bool *mlooptag)
{
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const MPoly *mp;
int (*scol)[4];
int i, j;
bool has_shared = false;
/* if no mloopcol: do not do */
/* if mtexpoly: only the involved faces, otherwise all */
if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) return;
scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol");
for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
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;
scol[ml->v][1] += lcol->g;
scol[ml->v][2] += lcol->b;
scol[ml->v][3] += 1;
has_shared = 1;
}
}
}
if (has_shared) {
for (i = 0; i < me->totvert; i++) {
if (scol[i][3] != 0) {
scol[i][0] = divide_round_i(scol[i][0], scol[i][3]);
scol[i][1] = divide_round_i(scol[i][1], scol[i][3]);
scol[i][2] = divide_round_i(scol[i][2], scol[i][3]);
}
}
for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
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]) {
lcol->r = scol[ml->v][0];
lcol->g = scol[ml->v][1];
lcol->b = scol[ml->v][2];
}
}
}
}
}
MEM_freeN(scol);
}
static bool vertex_color_smooth(Object *ob)
{
Mesh *me;
const MPoly *mp;
int i, j;
bool *mlooptag;
if (((me = BKE_mesh_from_object(ob)) == NULL) ||
(ED_mesh_color_ensure(me, NULL) == false))
{
return false;
}
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++) {
const MLoop *ml = me->mloop + mp->loopstart;
int ml_index = mp->loopstart;
if (use_face_sel && !(mp->flag & ME_FACE_SEL))
continue;
for (j = 0; j < mp->totloop; j++, ml_index++, ml++) {
mlooptag[ml_index] = true;
}
}
/* remove stale me->mcol, will be added later */
BKE_mesh_tessface_clear(me);
vertex_color_smooth_looptag(me, mlooptag);
MEM_freeN(mlooptag);
DAG_id_tag_update(&me->id, 0);
return true;
}
static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obact = CTX_data_active_object(C);
if (vertex_color_smooth(obact)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Smooth Vertex Colors";
ot->idname = "PAINT_OT_vertex_color_smooth";
ot->description = "Smooth colors across vertices";
/* api callbacks */
ot->exec = vertex_color_smooth_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Vertex Color Transformation Operators
* \{ */
struct VPaintTx_BrightContrastData {
/* pre-calculated */
float gain;
float offset;
};
static void vpaint_tx_brightness_contrast(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_BrightContrastData *data = user_data;
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * col[i] + data->offset;
}
}
static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
float gain, offset;
{
float brightness = RNA_float_get(op->ptr, "brightness");
float contrast = RNA_float_get(op->ptr, "contrast");
brightness /= 100.0f;
float delta = contrast / 200.0f;
gain = 1.0f - delta * 2.0f;
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
* Extracted of OpenCV demhist.c
*/
if (contrast > 0) {
gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
offset = gain * (brightness - delta);
}
else {
delta *= -1;
offset = gain * (brightness + delta);
}
}
const struct VPaintTx_BrightContrastData user_data = {
.gain = gain,
.offset = offset,
};
if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Vertex Paint Bright/Contrast";
ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
ot->description = "Adjust vertex color brightness/contrast";
/* api callbacks */
ot->exec = vertex_color_brightness_contrast_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
const float min = -100, max = +100;
prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
RNA_def_property_ui_range(prop, min, max, 1, 1);
}
struct VPaintTx_HueSatData {
float hue;
float sat;
float val;
};
static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_HueSatData *data = user_data;
float hsv[3];
rgb_to_hsv_v(col, hsv);
hsv[0] += (data->hue - 0.5f);
if (hsv[0] > 1.0f) {
hsv[0] -= 1.0f;
}
else if (hsv[0] < 0.0f) {
hsv[0] += 1.0f;
}
hsv[1] *= data->sat;
hsv[2] *= data->val;
hsv_to_rgb_v(hsv, r_col);
}
static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
const struct VPaintTx_HueSatData user_data = {
.hue = RNA_float_get(op->ptr, "h"),
.sat = RNA_float_get(op->ptr, "s"),
.val = RNA_float_get(op->ptr, "v"),
};
if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Hue Saturation Value";
ot->idname = "PAINT_OT_vertex_color_hsv";
ot->description = "Adjust vertex color HSV values";
/* api callbacks */
ot->exec = vertex_color_hsv_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
}
static void vpaint_tx_invert(const float col[3], const void *UNUSED(user_data), float r_col[3])
{
for (int i = 0; i < 3; i++) {
r_col[i] = 1.0f - col[i];
}
}
static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obact = CTX_data_active_object(C);
if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Invert";
ot->idname = "PAINT_OT_vertex_color_invert";
ot->description = "Invert RGB values";
/* api callbacks */
ot->exec = vertex_color_invert_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
struct VPaintTx_LevelsData {
float gain;
float offset;
};
static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3])
{
const struct VPaintTx_LevelsData *data = user_data;
for (int i = 0; i < 3; i++) {
r_col[i] = data->gain * (col[i] + data->offset);
}
}
static int vertex_color_levels_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
const struct VPaintTx_LevelsData user_data = {
.gain = RNA_float_get(op->ptr, "gain"),
.offset = RNA_float_get(op->ptr, "offset"),
};
if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_vertex_color_levels(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Paint Levels";
ot->idname = "PAINT_OT_vertex_color_levels";
ot->description = "Adjust levels of vertex colors";
/* api callbacks */
ot->exec = vertex_color_levels_exec;
ot->poll = vertex_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* params */
RNA_def_float(ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
RNA_def_float(ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
}
/** \} */

View File

@ -0,0 +1,648 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/sculpt_paint/paint_vertex_color_utils.c
* \ingroup edsculpt
*
* Intended for use by `paint_vertex.c` & `paint_vertex_color_ops.c`.
*/
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_math_base.h"
#include "BLI_math_color.h"
#include "IMB_colormanagement.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_mesh.h"
#include "ED_mesh.h"
#include "paint_intern.h" /* own include */
#define EPS_SATURATION 0.0005f
/**
* Apply callback to each vertex of the active vertex color layer.
*/
bool ED_vpaint_color_transform(
struct Object *ob,
VPaintTransform_Callback vpaint_tx_fn,
const void *user_data)
{
Mesh *me;
const MPoly *mp;
if (((me = BKE_mesh_from_object(ob)) == NULL) ||
(ED_mesh_color_ensure(me, NULL) == false))
{
return false;
}
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 (use_face_sel && !(mp->flag & ME_FACE_SEL)) {
continue;
}
for (int j = 0; j < mp->totloop; j++, lcol++) {
float col[3];
rgb_uchar_to_float(col, &lcol->r);
vpaint_tx_fn(col, user_data, col);
rgb_float_to_uchar(&lcol->r, col);
}
}
/* remove stale me->mcol, will be added later */
BKE_mesh_tessface_clear(me);
DAG_id_tag_update(&me->id, 0);
return true;
}
/* -------------------------------------------------------------------- */
/** \name Color Blending Modes
* \{ */
BLI_INLINE uint mcol_blend(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac;
uint col = 0;
if (fac == 0) {
return col1;
}
if (fac >= 255) {
return col2;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
/* 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 a1 = cp1[3] * cp1[3];
int r2 = cp2[0] * cp2[0];
int g2 = cp2[1] * cp2[1];
int b2 = cp2[2] * cp2[2];
int a2 = cp2[3] * cp2[3];
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] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * a1 + fac * a2), 255)));
return col;
}
BLI_INLINE uint mcol_add(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int temp;
uint col = 0;
if (fac == 0) {
return col1;
}
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;
temp = cp1[1] + divide_round_i((fac * cp2[1]), 255);
cp[1] = (temp > 254) ? 255 : temp;
temp = cp1[2] + divide_round_i((fac * cp2[2]), 255);
cp[2] = (temp > 254) ? 255 : temp;
temp = cp1[3] + divide_round_i((fac * cp2[3]), 255);
cp[3] = (temp > 254) ? 255 : temp;
return col;
}
BLI_INLINE uint mcol_sub(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int temp;
uint col = 0;
if (fac == 0) {
return col1;
}
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;
temp = cp1[1] - divide_round_i((fac * cp2[1]), 255);
cp[1] = (temp < 0) ? 0 : temp;
temp = cp1[2] - divide_round_i((fac * cp2[2]), 255);
cp[2] = (temp < 0) ? 0 : temp;
temp = cp1[3] - divide_round_i((fac * cp2[3]), 255);
cp[3] = (temp < 0) ? 0 : temp;
return col;
}
BLI_INLINE uint mcol_mul(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
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);
cp[1] = divide_round_i(mfac * cp1[1] * 255 + fac * cp2[1] * cp1[1], 255 * 255);
cp[2] = divide_round_i(mfac * cp1[2] * 255 + fac * cp2[2] * cp1[2], 255 * 255);
cp[3] = divide_round_i(mfac * cp1[3] * 255 + fac * cp2[3] * cp1[3], 255 * 255);
return col;
}
BLI_INLINE uint mcol_lighten(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac;
uint col = 0;
if (fac == 0) {
return col1;
}
else if (fac >= 255) {
return col2;
}
mfac = 255 - fac;
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 */
if (IMB_colormanagement_get_luminance_byte(cp1) > IMB_colormanagement_get_luminance_byte(cp2)) {
return col1;
}
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);
cp[3] = divide_round_i(mfac * cp1[3] + fac * cp2[3], 255);
return col;
}
BLI_INLINE uint mcol_darken(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac;
uint col = 0;
if (fac == 0) {
return col1;
}
else if (fac >= 255) {
return col2;
}
mfac = 255 - fac;
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 */
if (IMB_colormanagement_get_luminance_byte(cp1) < IMB_colormanagement_get_luminance_byte(cp2)) {
return col1;
}
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);
cp[3] = divide_round_i((mfac * cp1[3] + fac * cp2[3]), 255);
return col;
}
BLI_INLINE uint mcol_colordodge(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac,temp;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
temp = (cp2[0] == 255) ? 255 : min_ii((cp1[0] * 225) / (255 - cp2[0]), 255);
cp[0] = (mfac * cp1[0] + temp * fac) / 255;
temp = (cp2[1] == 255) ? 255 : min_ii((cp1[1] * 225) / (255 - cp2[1]), 255);
cp[1] = (mfac * cp1[1] + temp * fac) / 255;
temp = (cp2[2] == 255) ? 255 : min_ii((cp1[2] * 225 )/ (255 - cp2[2]), 255);
cp[2] = (mfac * cp1[2] + temp * fac) / 255;
temp = (cp2[3] == 255) ? 255 : min_ii((cp1[3] * 225) / (255 - cp2[3]), 255);
cp[3] = (mfac * cp1[3] + temp * fac) / 255;
return col;
}
BLI_INLINE uint mcol_difference(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac, temp;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
temp = abs(cp1[0] - cp2[0]);
cp[0] = (mfac * cp1[0] + temp * fac) / 255;
temp = abs(cp1[1] - cp2[1]);
cp[1] = (mfac * cp1[1] + temp * fac) / 255;
temp = abs(cp1[2] - cp2[2]);
cp[2] = (mfac * cp1[2] + temp * fac) / 255;
temp = abs(cp1[3] - cp2[3]);
cp[3] = (mfac * cp1[3] + temp * fac) / 255;
return col;
}
BLI_INLINE uint mcol_screen(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac, temp;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
temp = max_ii(255 - (((255 - cp1[0]) * (255 - cp2[0])) / 255), 0);
cp[0] = (mfac * cp1[0] + temp * fac) / 255;
temp = max_ii(255 - (((255 - cp1[1]) * (255 - cp2[1])) / 255), 0);
cp[1] = (mfac * cp1[1] + temp * fac) / 255;
temp = max_ii(255 - (((255 - cp1[2]) * (255 - cp2[2])) / 255), 0);
cp[2] = (mfac * cp1[2] + temp * fac) / 255;
temp = max_ii(255 - (((255 - cp1[3]) * (255 - cp2[3])) / 255), 0);
cp[3] = (mfac * cp1[3] + temp * fac) / 255;
return col;
}
BLI_INLINE uint mcol_hardlight(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac, temp;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
int i = 0;
for (i = 0; i < 4; i++) {
if (cp2[i] > 127) {
temp = 255 - ((255 - 2 * (cp2[i] - 127)) * (255 - cp1[i]) / 255);
}
else {
temp = (2 * cp2[i] * cp1[i]) >> 8;
}
cp[i] = min_ii((mfac * cp1[i] + temp * fac) / 255, 255);
}
return col;
}
BLI_INLINE uint mcol_overlay(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac, temp;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
int i = 0;
for (i = 0; i < 4; i++) {
if (cp1[i] > 127) {
temp = 255 - ((255 - 2 * (cp1[i] - 127)) * (255 - cp2[i]) / 255);
}
else {
temp = (2 * cp2[i] * cp1[i]) >> 8;
}
cp[i] = min_ii((mfac * cp1[i] + temp * fac) / 255, 255);
}
return col;
}
BLI_INLINE uint mcol_softlight(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac, temp;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
int i = 0;
for (i = 0; i < 4; i++) {
if (cp1[i] < 127) {
temp = ((2 * ((cp2[i] / 2) + 64)) * cp1[i]) / 255;
}
else {
temp = 255 - (2 * (255 - ((cp2[i] / 2) + 64)) * (255 - cp1[i]) / 255);
}
cp[i] = (temp * fac + cp1[i] * mfac) / 255;
}
return col;
}
BLI_INLINE uint mcol_exclusion(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac, temp;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
int i = 0;
for (i = 0; i < 4; i++) {
temp = 127 - ((2 * (cp1[i] - 127) * (cp2[i] - 127)) / 255);
cp[i] = (temp * fac + cp1[i] * mfac) / 255;
}
return col;
}
BLI_INLINE uint mcol_luminosity(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
float h1, s1, v1;
float h2, s2, v2;
float r, g, b;
rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1);
rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2);
v1 = v2;
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255;
cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255;
cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255;
cp[3] = ((int)(cp2[3]) * fac + mfac * cp1[3]) / 255;
return col;
}
BLI_INLINE uint mcol_saturation(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
float h1, s1, v1;
float h2, s2, v2;
float r, g, b;
rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1);
rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2);
if (s1 > EPS_SATURATION) {
s1 = s2;
}
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255;
cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255;
cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255;
return col;
}
BLI_INLINE uint mcol_hue(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
int mfac;
uint col = 0;
if (fac == 0) {
return col1;
}
mfac = 255 - fac;
cp1 = (uchar *)&col1;
cp2 = (uchar *)&col2;
cp = (uchar *)&col;
float h1, s1, v1;
float h2, s2, v2;
float r, g, b;
rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1);
rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2);
h1 = h2;
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255;
cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255;
cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255;
cp[3] = ((int)(cp2[3]) * fac + mfac * cp1[3]) / 255;
return col;
}
BLI_INLINE uint mcol_alpha_add(uint col1, int fac)
{
uchar *cp1, *cp;
int temp;
uint col = 0;
if (fac == 0) {
return col1;
}
cp1 = (uchar *)&col1;
cp = (uchar *)&col;
temp = cp1[3] + fac;
cp[3] = (temp > 254) ? 255 : temp;
return col;
}
BLI_INLINE uint mcol_alpha_sub(uint col1, int fac)
{
uchar *cp1, *cp;
int temp;
uint col = 0;
if (fac == 0) {
return col1;
}
cp1 = (uchar *)&col1;
cp = (uchar *)&col;
temp = cp1[3] - fac;
cp[3] = temp < 0 ? 0 : temp;
return col;
}
/* wpaint has 'ED_wpaint_blend_tool' */
uint ED_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);
case PAINT_BLEND_LIGHTEN: return mcol_lighten(col, paintcol, alpha_i);
case PAINT_BLEND_DARKEN: return mcol_darken(col, paintcol, alpha_i);
case PAINT_BLEND_COLORDODGE: return mcol_colordodge(col, paintcol, alpha_i);
case PAINT_BLEND_DIFFERENCE: return mcol_difference(col, paintcol, alpha_i);
case PAINT_BLEND_SCREEN: return mcol_screen(col, paintcol, alpha_i);
case PAINT_BLEND_HARDLIGHT: return mcol_hardlight(col, paintcol, alpha_i);
case PAINT_BLEND_OVERLAY: return mcol_overlay(col, paintcol, alpha_i);
case PAINT_BLEND_SOFTLIGHT: return mcol_softlight(col, paintcol, alpha_i);
case PAINT_BLEND_EXCLUSION: return mcol_exclusion(col, paintcol, alpha_i);
case PAINT_BLEND_LUMINOCITY: return mcol_luminosity(col, paintcol, alpha_i);
case PAINT_BLEND_SATURATION: return mcol_saturation(col, paintcol, alpha_i);
case PAINT_BLEND_HUE: return mcol_hue(col, paintcol, alpha_i);
/* non-color */
case PAINT_BLEND_ALPHA_SUB: return mcol_alpha_sub(col, alpha_i);
case PAINT_BLEND_ALPHA_ADD: return mcol_alpha_add(col, alpha_i);
default:
BLI_assert(0);
return 0;
}
}
/** \} */

View File

@ -0,0 +1,861 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/sculpt_paint/paint_vertex_weight_ops.c
* \ingroup edsculpt
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_array_utils.h"
#include "BLI_bitmap.h"
#include "BLI_task.h"
#include "BLI_string_utils.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_colormanagement.h"
//#include "DNA_armature_types.h"
#include "DNA_mesh_types.h"
#include "DNA_particle_types.h"
#include "DNA_brush_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "BKE_DerivedMesh.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_modifier.h"
#include "BKE_object_deform.h"
#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_colortools.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_armature.h"
#include "ED_mesh.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "paint_intern.h" /* own include */
/* -------------------------------------------------------------------- */
/** \name Store Previous Weights
*
* Use to avoid feedback loop w/ mirrored edits.
* \{ */
struct WPaintPrev {
/* previous vertex weights */
struct MDeformVert *wpaint_prev;
/* allocation size of prev buffers */
int tot;
};
static void wpaint_prev_init(struct WPaintPrev *wpp)
{
wpp->wpaint_prev = NULL;
wpp->tot = 0;
}
static void wpaint_prev_create(struct WPaintPrev *wpp, MDeformVert *dverts, int dcount)
{
wpaint_prev_init(wpp);
if (dverts && dcount) {
wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
wpp->tot = dcount;
BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount);
}
}
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;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Weight from Bones Operator
* \{ */
static int weight_from_bones_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
}
static int weight_from_bones_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Object *armob = modifiers_isDeformedByArmature(ob);
Mesh *me = ob->data;
int type = RNA_enum_get(op->ptr, "type");
create_vgroups_from_armature(op->reports, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X));
DAG_id_tag_update(&me->id, 0);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
return OPERATOR_FINISHED;
}
void PAINT_OT_weight_from_bones(wmOperatorType *ot)
{
static EnumPropertyItem type_items[] = {
{ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"},
{ARM_GROUPS_ENVELOPE, "ENVELOPES", 0, "From Envelopes", "Weights from envelopes with user defined radius"},
{0, NULL, 0, NULL, NULL}};
/* identifiers */
ot->name = "Weight from Bones";
ot->idname = "PAINT_OT_weight_from_bones";
ot->description = "Set the weights of the groups matching the attached armature's selected bones, "
"using the distance between the vertices and the bones";
/* api callbacks */
ot->exec = weight_from_bones_exec;
ot->invoke = WM_menu_invoke;
ot->poll = weight_from_bones_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Sample Weight Operator
* \{ */
/* sets wp->weight to the closest weight value to vertex */
/* note: we cant sample frontbuf, weight colors are interpolated too unpredictable */
static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ViewContext vc;
Mesh *me;
bool changed = false;
view3d_set_viewcontext(C, &vc);
me = BKE_mesh_from_object(vc.obact);
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;
uint index;
view3d_operator_needs_opengl(C);
ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
if (use_vert_sel) {
if (ED_mesh_pick_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
v_idx_best = index;
}
}
else {
if (ED_mesh_pick_face_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
v_idx_best = index;
}
else if (ED_mesh_pick_face(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
/* this relies on knowning the internal worksings of ED_mesh_pick_face_vert() */
BKE_report(op->reports, RPT_WARNING, "The modifier used does not support deformed locations");
}
}
if (v_idx_best != -1) { /* should always be valid */
ToolSettings *ts = vc.scene->toolsettings;
Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
const int vgroup_active = vc.obact->actdef - 1;
float vgroup_weight = defvert_find_weight(&me->dvert[v_idx_best], vgroup_active);
/* use combined weight in multipaint mode, since that's what is displayed to the user in the colors */
if (ts->multipaint) {
int defbase_tot_sel;
const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
bool *defbase_sel = BKE_object_defgroup_selected_get(vc.obact, defbase_tot, &defbase_tot_sel);
if (defbase_tot_sel > 1) {
if (me->editflag & ME_EDIT_MIRROR_X) {
BKE_object_defgroup_mirror_selection(
vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
}
vgroup_weight = BKE_defvert_multipaint_collective_weight(
&me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, ts->auto_normalize);
/* if autonormalize is enabled, but weights are not normalized, the value can exceed 1 */
CLAMP(vgroup_weight, 0.0f, 1.0f);
}
MEM_freeN(defbase_sel);
}
BKE_brush_weight_set(vc.scene, brush, vgroup_weight);
changed = true;
}
}
if (changed) {
/* not really correct since the brush didnt change, but redraws the toolbar */
WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); /* ts->wpaint->paint.brush */
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_weight_sample(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Weight Paint Sample Weight";
ot->idname = "PAINT_OT_weight_sample";
ot->description = "Use the mouse to sample a weight in the 3D view";
/* api callbacks */
ot->invoke = weight_sample_invoke;
ot->poll = weight_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Weight Paint Sample Group Operator
* \{ */
/* samples cursor location, and gives menu with vertex groups to activate */
static bool weight_paint_sample_enum_itemf__helper(const MDeformVert *dvert, const int defbase_tot, int *groups)
{
/* this func fills in used vgroup's */
bool found = false;
int i = dvert->totweight;
MDeformWeight *dw;
for (dw = dvert->dw; i > 0; dw++, i--) {
if (dw->def_nr < defbase_tot) {
groups[dw->def_nr] = true;
found = true;
}
}
return found;
}
static EnumPropertyItem *weight_paint_sample_enum_itemf(
bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
if (C) {
wmWindow *win = CTX_wm_window(C);
if (win && win->eventstate) {
ViewContext vc;
Mesh *me;
view3d_set_viewcontext(C, &vc);
me = BKE_mesh_from_object(vc.obact);
if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) {
const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
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;
uint index;
const int mval[2] = {
win->eventstate->x - vc.ar->winrct.xmin,
win->eventstate->y - vc.ar->winrct.ymin,
};
view3d_operator_needs_opengl(C);
ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
if (use_vert_sel) {
if (ED_mesh_pick_vert(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
MDeformVert *dvert = &me->dvert[index];
found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
}
}
else {
if (ED_mesh_pick_face(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
const MPoly *mp = &me->mpoly[index];
uint fidx = mp->totloop - 1;
do {
MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v];
found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
} while (fidx--);
}
}
if (found == false) {
MEM_freeN(groups);
}
else {
EnumPropertyItem *item = NULL, item_tmp = {0};
int totitem = 0;
int i = 0;
bDeformGroup *dg;
for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) {
if (groups[i]) {
item_tmp.identifier = item_tmp.name = dg->name;
item_tmp.value = i;
RNA_enum_item_add(&item, &totitem, &item_tmp);
}
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
MEM_freeN(groups);
return item;
}
}
}
}
return DummyRNA_NULL_items;
}
static int weight_sample_group_exec(bContext *C, wmOperator *op)
{
int type = RNA_enum_get(op->ptr, "group");
ViewContext vc;
view3d_set_viewcontext(C, &vc);
BLI_assert(type + 1 >= 0);
vc.obact->actdef = type + 1;
DAG_id_tag_update(&vc.obact->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact);
return OPERATOR_FINISHED;
}
/* TODO, we could make this a menu into OBJECT_OT_vertex_group_set_active rather than its own operator */
void PAINT_OT_weight_sample_group(wmOperatorType *ot)
{
PropertyRNA *prop = NULL;
/* identifiers */
ot->name = "Weight Paint Sample Group";
ot->idname = "PAINT_OT_weight_sample_group";
ot->description = "Select one of the vertex groups available under current mouse position";
/* api callbacks */
ot->exec = weight_sample_group_exec;
ot->invoke = WM_menu_invoke;
ot->poll = weight_paint_mode_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
/* keyingset to use (dynamic enum) */
prop = RNA_def_enum(ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Weight Set Operator
* \{ */
/* fills in the selected faces with the current weight and vertex group */
static bool weight_paint_set(Object *ob, float paintweight)
{
Mesh *me = ob->data;
const MPoly *mp;
MDeformWeight *dw, *dw_prev;
int vgroup_active, vgroup_mirror = -1;
uint index;
const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
/* mutually exclusive, could be made into a */
const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me);
if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) {
return false;
}
vgroup_active = ob->actdef - 1;
/* if mirror painting, find the other group */
if (me->editflag & ME_EDIT_MIRROR_X) {
vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active);
}
struct WPaintPrev wpp;
wpaint_prev_create(&wpp, me->dvert, me->totvert);
for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
uint fidx = mp->totloop - 1;
if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) {
continue;
}
do {
uint vidx = me->mloop[mp->loopstart + fidx].v;
if (!me->dvert[vidx].flag) {
if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
continue;
}
dw = defvert_verify_index(&me->dvert[vidx], vgroup_active);
if (dw) {
dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active);
dw_prev->weight = dw->weight; /* set the undo weight */
dw->weight = paintweight;
if (me->editflag & ME_EDIT_MIRROR_X) { /* x mirror painting */
int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology);
if (j >= 0) {
/* copy, not paint again */
if (vgroup_mirror != -1) {
dw = defvert_verify_index(me->dvert + 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(wpp.wpaint_prev + j, vgroup_active);
}
dw_prev->weight = dw->weight; /* set the undo weight */
dw->weight = paintweight;
}
}
}
me->dvert[vidx].flag = 1;
}
} while (fidx--);
}
{
MDeformVert *dv = me->dvert;
for (index = me->totvert; index != 0; index--, dv++) {
dv->flag = 0;
}
}
wpaint_prev_destroy(&wpp);
DAG_id_tag_update(&me->id, 0);
return true;
}
static int weight_paint_set_exec(bContext *C, wmOperator *op)
{
struct Scene *scene = CTX_data_scene(C);
Object *obact = CTX_data_active_object(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
float vgroup_weight = BKE_brush_weight_get(scene, brush);
if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, NULL) == false) {
return OPERATOR_CANCELLED;
}
if (weight_paint_set(obact, vgroup_weight)) {
ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
return OPERATOR_FINISHED;
}
else {
return OPERATOR_CANCELLED;
}
}
void PAINT_OT_weight_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Weight";
ot->idname = "PAINT_OT_weight_set";
ot->description = "Fill the active vertex group with the current paint weight";
/* api callbacks */
ot->exec = weight_paint_set_exec;
ot->poll = mask_paint_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Interactive Weight Gradient Operator
* \{ */
/* *** VGroups Gradient *** */
typedef struct DMGradient_vertStore {
float sco[2];
float weight_orig;
enum {
VGRAD_STORE_NOP = 0,
VGRAD_STORE_DW_EXIST = (1 << 0)
} 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;
Mesh *me;
Brush *brush;
const float *sco_start; /* [2] */
const float *sco_end; /* [2] */
float sco_line_div; /* store (1.0f / len_v2v2(sco_start, sco_end)) */
int def_nr;
bool is_init;
DMGradient_vertStoreBase *vert_cache;
/* only for init */
BLI_bitmap *vert_visit;
/* options */
short use_select;
short type;
float weightpaint;
} DMGradient_userData;
static void gradientVert_update(DMGradient_userData *grad_data, int index)
{
Mesh *me = grad_data->me;
DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
float alpha;
if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end);
}
else {
BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL);
alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div;
}
/* no need to clamp 'alpha' yet */
/* adjust weight */
alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f);
if (alpha != 0.0f) {
MDeformVert *dv = &me->dvert[index];
MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr);
// dw->weight = alpha; // testing
int tool = grad_data->brush->vertexpaint_tool;
float testw;
/* init if we just added */
testw = ED_wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha);
CLAMP(testw, 0.0f, 1.0f);
dw->weight = testw;
}
else {
MDeformVert *dv = &me->dvert[index];
if (vs->flag & VGRAD_STORE_DW_EXIST) {
/* normally we NULL check, but in this case we know it exists */
MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
dw->weight = vs->weight_orig;
}
else {
/* wasn't originally existing, remove */
MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
if (dw) {
defvert_remove_group(dv, dw);
}
}
}
}
static void gradientVertUpdate__mapFunc(
void *userData, int index, const float UNUSED(co[3]),
const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
{
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->elem[index];
if (vs->sco[0] != FLT_MAX) {
gradientVert_update(grad_data, index);
}
}
}
static void gradientVertInit__mapFunc(
void *userData, int index, const float co[3],
const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
{
DMGradient_userData *grad_data = userData;
Mesh *me = grad_data->me;
if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
/* run first pass only,
* the screen coords of the verts need to be cached because
* 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->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];
const MDeformWeight *dw;
dw = defvert_find_index(dv, grad_data->def_nr);
if (dw) {
vs->weight_orig = dw->weight;
vs->flag = VGRAD_STORE_DW_EXIST;
}
else {
vs->weight_orig = 0.0f;
vs->flag = VGRAD_STORE_NOP;
}
BLI_BITMAP_ENABLE(grad_data->vert_visit, index);
gradientVert_update(grad_data, index);
}
else {
/* no go */
copy_v2_fl(vs->sco, FLT_MAX);
}
}
}
}
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! */
do_gesture_free = true;
ret &= ~OPERATOR_RUNNING_MODAL;
ret |= OPERATOR_FINISHED;
}
}
if (ret & OPERATOR_CANCELLED) {
Object *ob = CTX_data_active_object(C);
Mesh *me = ob->data;
if (vert_cache->wpp.wpaint_prev) {
BKE_defvert_array_free_elems(me->dvert, me->totvert);
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) {
wpaint_prev_destroy(&vert_cache->wpp);
}
if (do_gesture_free) {
WM_gesture_straightline_cancel(C, op);
}
return ret;
}
static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
{
wmGesture *gesture = op->customdata;
DMGradient_vertStoreBase *vert_cache;
struct ARegion *ar = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Mesh *me = ob->data;
int x_start = RNA_int_get(op->ptr, "xstart");
int y_start = RNA_int_get(op->ptr, "ystart");
int x_end = RNA_int_get(op->ptr, "xend");
int y_end = RNA_int_get(op->ptr, "yend");
float sco_start[2] = {x_start, y_start};
float sco_end[2] = {x_end, y_end};
const bool is_interactive = (gesture != NULL);
DerivedMesh *dm = mesh_get_derived_final(scene, ob, scene->customdata_mask);
DMGradient_userData data = {NULL};
if (is_interactive) {
if (gesture->userdata == NULL) {
gesture->userdata = MEM_mallocN(
sizeof(DMGradient_vertStoreBase) +
(sizeof(DMGradient_vertStore) * me->totvert),
__func__);
data.is_init = true;
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) {
BKE_mesh_flush_select_from_polys(me);
}
}
vert_cache = gesture->userdata;
}
else {
if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
return OPERATOR_CANCELLED;
}
data.is_init = true;
vert_cache = MEM_mallocN(
sizeof(DMGradient_vertStoreBase) +
(sizeof(DMGradient_vertStore) * me->totvert),
__func__);
}
data.ar = ar;
data.scene = scene;
data.me = ob->data;
data.sco_start = sco_start;
data.sco_end = sco_end;
data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end);
data.def_nr = ob->actdef - 1;
data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL));
data.vert_cache = vert_cache;
data.vert_visit = NULL;
data.type = RNA_enum_get(op->ptr, "type");
{
ToolSettings *ts = CTX_data_tool_settings(C);
VPaint *wp = ts->wpaint;
struct Brush *brush = BKE_paint_brush(&wp->paint);
curvemapping_initialize(brush->curve);
data.brush = brush;
data.weightpaint = BKE_brush_weight_get(scene, brush);
}
ED_view3d_init_mats_rv3d(ob, ar->regiondata);
if (data.is_init) {
data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__);
dm->foreachMappedVert(dm, gradientVertInit__mapFunc, &data, DM_FOREACH_NOP);
MEM_freeN(data.vert_visit);
data.vert_visit = NULL;
}
else {
dm->foreachMappedVert(dm, gradientVertUpdate__mapFunc, &data, DM_FOREACH_NOP);
}
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
if (is_interactive == false) {
MEM_freeN(vert_cache);
}
return OPERATOR_FINISHED;
}
static int paint_weight_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int ret;
if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
return OPERATOR_CANCELLED;
}
ret = WM_gesture_straightline_invoke(C, op, event);
if (ret & OPERATOR_RUNNING_MODAL) {
struct ARegion *ar = CTX_wm_region(C);
if (ar->regiontype == RGN_TYPE_WINDOW) {
/* TODO, hardcoded, extend WM_gesture_straightline_ */
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
wmGesture *gesture = op->customdata;
gesture->mode = 1;
}
}
}
return ret;
}
void PAINT_OT_weight_gradient(wmOperatorType *ot)
{
/* defined in DNA_space_types.h */
static EnumPropertyItem gradient_types[] = {
{WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""},
{WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""},
{0, NULL, 0, NULL, NULL}
};
PropertyRNA *prop;
/* identifiers */
ot->name = "Weight Gradient";
ot->idname = "PAINT_OT_weight_gradient";
ot->description = "Draw a line to apply a weight gradient to selected vertices";
/* api callbacks */
ot->invoke = paint_weight_gradient_invoke;
ot->modal = paint_weight_gradient_modal;
ot->exec = paint_weight_gradient_exec;
ot->poll = weight_paint_poll;
ot->cancel = WM_gesture_straightline_cancel;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
}
/** \} */

View File

@ -0,0 +1,212 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/sculpt_paint/paint_vertex_weight_utils.c
* \ingroup edsculpt
*
* Intended for use by `paint_vertex.c` & `paint_vertex_weight_ops.c`.
*/
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string_utils.h"
#include "DNA_armature_types.h"
#include "DNA_mesh_types.h"
#include "DNA_scene_types.h"
#include "DNA_brush_types.h"
#include "DNA_object_types.h"
#include "BKE_action.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object_deform.h"
#include "BKE_report.h"
#include "WM_api.h"
#include "WM_types.h"
#include "paint_intern.h" /* own include */
/* -------------------------------------------------------------------- */
/** \name Weight Paint Sanity Checks
* \{ */
/* ensure we have data on wpaint start, add if needed */
bool ED_wpaint_ensure_data(
bContext *C, struct ReportList *reports,
enum eWPaintFlag flag, struct WPaintVGroupIndex *vgroup_index)
{
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Mesh *me = BKE_mesh_from_object(ob);
if (vgroup_index) {
vgroup_index->active = -1;
vgroup_index->mirror = -1;
}
if (scene->obedit) {
return false;
}
if (me == NULL || me->totpoly == 0) {
return false;
}
/* if nothing was added yet, we make dverts and a vertex deform group */
if (!me->dvert) {
BKE_object_defgroup_data_create(&me->id);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
}
/* this happens on a Bone select, when no vgroup existed yet */
if (ob->actdef <= 0) {
Object *modob;
if ((modob = modifiers_isDeformedByArmature(ob))) {
Bone *actbone = ((bArmature *)modob->data)->act_bone;
if (actbone) {
bPoseChannel *pchan = BKE_pose_channel_find_name(modob->pose, actbone->name);
if (pchan) {
bDeformGroup *dg = defgroup_find_name(ob, pchan->name);
if (dg == NULL) {
dg = BKE_object_defgroup_add_name(ob, pchan->name); /* sets actdef */
}
else {
int actdef = 1 + BLI_findindex(&ob->defbase, dg);
BLI_assert(actdef >= 0);
ob->actdef = actdef;
}
}
}
}
}
if (BLI_listbase_is_empty(&ob->defbase)) {
BKE_object_defgroup_add(ob);
}
/* ensure we don't try paint onto an invalid group */
if (ob->actdef <= 0) {
BKE_report(reports, RPT_WARNING, "No active vertex group for painting, aborting");
return false;
}
if (vgroup_index) {
vgroup_index->active = ob->actdef - 1;
}
if (flag & WPAINT_ENSURE_MIRROR) {
if (me->editflag & ME_EDIT_MIRROR_X) {
int mirror = ED_wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1);
if (vgroup_index) {
vgroup_index->mirror = mirror;
}
}
}
return true;
}
/** \} */
/* mirror_vgroup is set to -1 when invalid */
int ED_wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active)
{
bDeformGroup *defgroup = BLI_findlink(&ob->defbase, vgroup_active);
if (defgroup) {
int mirrdef;
char name_flip[MAXBONENAME];
BLI_string_flip_side_name(name_flip, defgroup->name, false, sizeof(name_flip));
mirrdef = defgroup_name_index(ob, name_flip);
if (mirrdef == -1) {
if (BKE_defgroup_new(ob, name_flip)) {
mirrdef = BLI_listbase_count(&ob->defbase) - 1;
}
}
/* curdef should never be NULL unless this is
* a lamp and BKE_object_defgroup_add_name fails */
return mirrdef;
}
return -1;
}
/* -------------------------------------------------------------------- */
/** \name Weight Blending Modes
* \{ */
BLI_INLINE float wval_blend(const float weight, const float paintval, const float alpha)
{
const float talpha = min_ff(alpha, 1.0f); /* blending with values over 1 doesn't make sense */
return (paintval * talpha) + (weight * (1.0f - talpha));
}
BLI_INLINE float wval_add(const float weight, const float paintval, const float alpha)
{
return weight + (paintval * alpha);
}
BLI_INLINE float wval_sub(const float weight, const float paintval, const float alpha)
{
return weight - (paintval * alpha);
}
BLI_INLINE float wval_mul(const float weight, const float paintval, const float alpha)
{ /* first mul, then blend the fac */
return ((1.0f - alpha) + (alpha * paintval)) * weight;
}
BLI_INLINE float wval_lighten(const float weight, const float paintval, const float alpha)
{
return (weight < paintval) ? wval_blend(weight, paintval, alpha) : weight;
}
BLI_INLINE float wval_darken(const float weight, const float paintval, const float alpha)
{
return (weight > paintval) ? wval_blend(weight, paintval, alpha) : weight;
}
/* vpaint has 'vpaint_blend_tool' */
/* result is not clamped from [0-1] */
float ED_wpaint_blend_tool(
const int tool,
/* dw->weight */
const float weight,
const float paintval, const float alpha)
{
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);
case PAINT_BLEND_MUL: return wval_mul(weight, paintval, alpha);
case PAINT_BLEND_LIGHTEN: return wval_lighten(weight, paintval, alpha);
case PAINT_BLEND_DARKEN: return wval_darken(weight, paintval, alpha);
default:
BLI_assert(0);
return 0.0f;
}
}
/** \} */

View File

@ -55,12 +55,12 @@ int sculpt_poll(struct bContext *C);
int sculpt_poll_view3d(struct bContext *C);
/* Stroke */
bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]);
bool sculpt_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]);
/* Dynamic topology */
void sculpt_pbvh_clear(Object *ob);
void sculpt_dyntopo_node_layers_add(struct SculptSession *ss);
void sculpt_update_after_dynamic_topology_toggle(bContext *C);
void sculpt_update_after_dynamic_topology_toggle(struct bContext *C);
void sculpt_dynamic_topology_enable(struct bContext *C);
void sculpt_dynamic_topology_disable(struct bContext *C,
struct SculptUndoNode *unode);
@ -129,7 +129,7 @@ struct SculptRakeData {
/* Single struct used by all BLI_task threaded callbacks, let's avoid adding 10's of those... */
typedef struct SculptThreadedTaskData {
bContext *C;
struct bContext *C;
struct Sculpt *sd;
struct Object *ob;
struct Brush *brush;