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:
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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue