Woot woot commit.

Stencil style texture mapping. Ready for field testing and user feedback.

This commit adds stencil like brushes, like those that existed on old ptex branch.
(with the exception of clip colour)
To control the position of the stencil, you use

Q: translation
Shift - Q: scaling
Ctrl - Q: rotation

There's extra work that has been done to make this work:

* Support for coloured overlay in vertex/texture painting
* Also made A button do stroke mode selection like in sculpt mode,
when mask painting is inactive.

There are some TODOs to work on during bcon3:

* Support tiled and stencil mode in 2D painting. Support alpha textures also.
* Tidy up overlay code. There's some confusion there due
to the way we use the primary brush texture sometimes for alpha, other times
for colour control.

WIP design docs will be in

http://wiki.blender.org/index.php/User:Psy-Fi/New_Brush_Tool_Design
This commit is contained in:
Antony Riakiotakis 2013-03-31 00:38:50 +00:00
parent 68ac08681a
commit 1fea6220a2
14 changed files with 368 additions and 47 deletions

@ -42,7 +42,7 @@ extern "C" {
* and keep comment above the defines.
* Use STRINGIFY() rather than defining with quotes */
#define BLENDER_VERSION 266
#define BLENDER_SUBVERSION 4
#define BLENDER_SUBVERSION 5
/* 262 was the last editmesh release but it has compatibility code for bmesh data */
#define BLENDER_MINVERSION 262

@ -121,6 +121,12 @@ static void brush_defaults(Brush *brush)
brush->sub_col[0] = 0.39; /* subtract mode color is light blue */
brush->sub_col[1] = 0.39;
brush->sub_col[2] = 1.00;
brush->stencil_pos[0] = 256;
brush->stencil_pos[1] = 256;
brush->stencil_dimension[0] = 256;
brush->stencil_dimension[1] = 256;
}
/* Datablock add/copy/free/make_local */
@ -518,11 +524,45 @@ float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br,
hasrgb = externtex(mtex, point, &intensity,
rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool);
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) {
float rotation = -mtex->rot;
float point_2d[2] = {point[0], point[1]};
float x = 0.0f, y = 0.0f; /* Quite warnings */
float co[3];
x = point_2d[0] - br->stencil_pos[0];
y = point_2d[1] - br->stencil_pos[1];
if (rotation > 0.001f || rotation < -0.001f) {
const float angle = atan2f(y, x) + rotation;
const float flen = sqrtf(x * x + y * y);
x = flen * cosf(angle);
y = flen * sinf(angle);
}
if (fabs(x) > br->stencil_dimension[0] || fabs(y) > br->stencil_dimension[1]) {
rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0.0;
return 0.0;
}
x /= (br->stencil_dimension[0]);
y /= (br->stencil_dimension[1]);
x *= br->mtex.size[0];
y *= br->mtex.size[1];
co[0] = x + br->mtex.ofs[0];
co[1] = y + br->mtex.ofs[1];
co[2] = 0.0f;
hasrgb = externtex(mtex, co, &intensity,
rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool);
}
else {
float rotation = -mtex->rot;
float point_2d[2] = {point[0], point[1]};
float x = 0.0f, y = 0.0f; /* Quite warnings */
float radius = 1.0f; /* Quite warnings */
float invradius = 1.0f; /* Quite warnings */
float co[3];
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
@ -534,13 +574,13 @@ float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br,
y = point_2d[1] - ups->tex_mouse[1];
/* use pressure adjusted size for fixed mode */
radius = ups->pixel_radius;
invradius = 1.0/ups->pixel_radius;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
/* leave the coordinates relative to the screen */
/* use unadjusted size for tiled mode */
radius = BKE_brush_size_get(scene, br);
invradius = 1.0/BKE_brush_size_get(scene, br);
x = point_2d[0];
y = point_2d[1];
@ -551,11 +591,11 @@ float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br,
x = point_2d[0] - ups->tex_mouse[0];
y = point_2d[1] - ups->tex_mouse[1];
radius = ups->pixel_radius;
invradius = 1.0/ups->pixel_radius;
}
x /= radius;
y /= radius;
x *= invradius;
y *= invradius;
/* it is probably worth optimizing for those cases where
* the texture is not rotated by skipping the calls to

@ -92,6 +92,7 @@ MINLINE void add_v3_fl(float r[3], float f);
MINLINE void add_v4_fl(float r[4], float f);
MINLINE void add_v2_v2(float r[2], const float a[2]);
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2]);
MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2]);
MINLINE void add_v3_v3(float r[3], const float a[3]);
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3]);
MINLINE void add_v4_v4(float r[4], const float a[4]);
@ -99,6 +100,7 @@ MINLINE void add_v4_v4v4(float r[4], const float a[4], const float b[4]);
MINLINE void sub_v2_v2(float r[2], const float a[2]);
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2]);
MINLINE void sub_v2_v2v2_int(int r[2], const int a[2], const int b[2]);
MINLINE void sub_v3_v3(float r[3], const float a[3]);
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3]);
MINLINE void sub_v4_v4(float r[4], const float a[4]);

@ -271,6 +271,12 @@ MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
r[1] = a[1] + b[1];
}
MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2])
{
r[0] = a[0] + b[0];
r[1] = a[1] + b[1];
}
MINLINE void add_v3_v3(float r[3], const float a[3])
{
r[0] += a[0];
@ -313,6 +319,12 @@ MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
r[1] = a[1] - b[1];
}
MINLINE void sub_v2_v2v2_int(int r[2], const int a[2], const int b[2])
{
r[0] = a[0] - b[0];
r[1] = a[1] - b[1];
}
MINLINE void sub_v3_v3(float r[3], const float a[3])
{
r[0] -= a[0];

@ -9077,7 +9077,18 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
}
if (main->versionfile < 267) {
//if(!DNA_struct_elem_find(fd->filesdna, "Brush", "int", "stencil_pos")) {
Brush *brush;
for (brush = main->brush.first; brush; brush = brush->id.next) {
if (brush->stencil_dimension[0] == 0) {
brush->stencil_dimension[0] = 256;
brush->stencil_dimension[1] = 256;
brush->stencil_pos[0] = 256;
brush->stencil_pos[1] = 256;
}
}
/* TIP: to initialize new variables added, use the new function
DNA_struct_elem_find(fd->filesdna, "structname", "typename", "varname")
example:

@ -117,7 +117,7 @@ static void make_snap(Snapshot *snap, Brush *brush, ViewContext *vc)
snap->winy = vc->ar->winy;
}
static int load_tex(Brush *br, ViewContext *vc, float zoom)
static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col)
{
static GLuint overlay_texture = 0;
static int init = 0;
@ -126,12 +126,14 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom)
static Snapshot snap;
static int old_size = -1;
static int old_zoom = -1;
static bool old_col = -1;
GLubyte *buffer = NULL;
int size;
int j;
int refresh;
int format = col? GL_RGBA : GL_ALPHA;
if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0;
@ -143,11 +145,15 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom)
!br->curve ||
br->curve->changed_timestamp != curve_changed_timestamp ||
old_zoom != zoom ||
old_col != col ||
!same_snap(&snap, br, vc);
if (refresh) {
struct ImagePool *pool = NULL;
const float rotation = -br->mtex.rot;
/* stencil is rotated later */
const float rotation = (br->mtex.brush_map_mode != MTEX_MAP_MODE_STENCIL)?
-br->mtex.rot : 0;
float radius = BKE_brush_size_get(vc->scene, br) * zoom;
if (br->mtex.tex && br->mtex.tex->preview)
@ -187,8 +193,10 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom)
old_size = size;
}
buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex");
if (col)
buffer = MEM_mallocN(sizeof(GLubyte) * size * size * 4, "load_tex");
else
buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex");
if (br->mtex.tex)
pool = BKE_image_pool_new();
@ -205,7 +213,6 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom)
int index = j * size + i;
float x;
float avg;
x = (float)i / size;
y = (float)j / size;
@ -224,7 +231,7 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom)
len = sqrtf(x * x + y * y);
if ((br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) || len <= 1) {
if (ELEM(br->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL) || len <= 1) {
/* it is probably worth optimizing for those cases where
* the texture is not rotated by skipping the calls to
* atan2, sqrtf, sin, and cos. */
@ -241,17 +248,40 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom)
x += br->mtex.ofs[0];
y += br->mtex.ofs[1];
avg = br->mtex.tex ? paint_get_tex_pixel(br, x, y, pool) : 1;
if (col) {
float rgba[4];
avg += br->texture_sample_bias;
if (br->mtex.tex)
paint_get_tex_pixel_col(&br->mtex, x, y, rgba, pool);
if (br->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW)
avg *= BKE_brush_curve_strength(br, len, 1); /* Falloff curve */
if (br->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW)
mul_v4_fl(rgba, BKE_brush_curve_strength(br, len, 1)); /* Falloff curve */
buffer[index] = 255 - (GLubyte)(255 * avg);
buffer[index*4] = rgba[0]*255;
buffer[index*4 + 1] = rgba[1]*255;
buffer[index*4 + 2] = rgba[2]*255;
buffer[index*4 + 3] = rgba[3]*255;
}
else {
float avg = br->mtex.tex ? paint_get_tex_pixel(&br->mtex, x, y, pool) : 1;
avg += br->texture_sample_bias;
if (br->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW)
avg *= BKE_brush_curve_strength(br, len, 1); /* Falloff curve */
buffer[index] = 255 - (GLubyte)(255 * avg);
}
}
else {
buffer[index] = 0;
if (col) {
buffer[index*4] = 0;
buffer[index*4 + 1] = 0;
buffer[index*4 + 2] = 0;
buffer[index*4 + 3] = 0;
}
else
buffer[index] = 0;
}
}
}
@ -269,16 +299,18 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom)
glBindTexture(GL_TEXTURE_2D, overlay_texture);
if (refresh) {
if (!init) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, size, size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
if (!init || (old_col != col)) {
glTexImage2D(GL_TEXTURE_2D, 0, format, size, size, 0, format, GL_UNSIGNED_BYTE, buffer);
init = 1;
}
else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, format, GL_UNSIGNED_BYTE, buffer);
}
if (buffer)
MEM_freeN(buffer);
old_col = col;
}
glEnable(GL_TEXTURE_2D);
@ -385,17 +417,21 @@ static int sculpt_get_brush_geometry(bContext *C, ViewContext *vc,
* have on brush strength */
/* TODO: sculpt only for now */
static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush,
ViewContext *vc, int x, int y, float zoom)
ViewContext *vc, int x, int y, float zoom, PaintMode mode)
{
rctf quad;
bool col;
/* check for overlay mode */
if (!(brush->flag & BRUSH_TEXTURE_OVERLAY) ||
!(ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))
if (brush->mtex.brush_map_mode != MTEX_MAP_MODE_STENCIL &&
(!(brush->flag & BRUSH_TEXTURE_OVERLAY) ||
!ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))
{
return;
}
col = ELEM3(mode, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D, PAINT_VERTEX)?
true: false;
/* save lots of GL state
* TODO: check on whether all of these are needed? */
glPushAttrib(GL_COLOR_BUFFER_BIT |
@ -409,7 +445,7 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush,
GL_VIEWPORT_BIT |
GL_TEXTURE_BIT);
if (load_tex(brush, vc, zoom)) {
if (load_tex(brush, vc, zoom, col)) {
glEnable(GL_BLEND);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@ -449,18 +485,36 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush,
quad.ymax = y + radius;
}
}
else {
else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED){
quad.xmin = 0;
quad.ymin = 0;
quad.xmax = BLI_rcti_size_x(&vc->ar->winrct);
quad.ymax = BLI_rcti_size_y(&vc->ar->winrct);
}
else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL) {
quad.xmin = -brush->stencil_dimension[0];
quad.ymin = -brush->stencil_dimension[1];
quad.xmax = brush->stencil_dimension[0];
quad.ymax = brush->stencil_dimension[1];
/* set quad color */
glColor4f(U.sculpt_paint_overlay_col[0],
U.sculpt_paint_overlay_col[1],
U.sculpt_paint_overlay_col[2],
brush->texture_overlay_alpha / 100.0f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(brush->stencil_pos[0], brush->stencil_pos[1], 0);
glRotatef(brush->mtex.rot/M_PI*180, 0, 0, 1);
glMatrixMode(GL_TEXTURE);
}
/* set quad color. Colored overlay does not get blending */
if (col)
glColor4f(1.0,
1.0,
1.0,
brush->texture_overlay_alpha / 100.0f);
else
glColor4f(U.sculpt_paint_overlay_col[0],
U.sculpt_paint_overlay_col[1],
U.sculpt_paint_overlay_col[2],
brush->texture_overlay_alpha / 100.0f);
/* draw textured quad */
glBegin(GL_QUADS);
@ -475,6 +529,11 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush,
glEnd();
glPopMatrix();
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL) {
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
}
glPopAttrib();
@ -519,6 +578,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
Paint *paint = paint_get_active_from_context(C);
Brush *brush = paint_brush(paint);
ViewContext vc;
PaintMode mode;
float final_radius;
float translation[2];
float outline_alpha, *outline_col;
@ -534,6 +594,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
get_imapaint_zoom(C, &zoomx, &zoomy);
zoomx = max_ff(zoomx, zoomy);
mode = paintmode_get_active_from_context(C);
/* set various defaults */
translation[0] = x;
@ -549,7 +610,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
ups->brush_rotation = 0.0;
/* draw overlay */
paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx);
paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode);
/* TODO: as sculpt and other paint modes are unified, this
* special mode of drawing will go away */

@ -39,6 +39,7 @@ struct Brush;
struct ImagePool;
struct ListBase;
struct Mesh;
struct MTex;
struct Object;
struct PaintStroke;
struct Paint;
@ -179,7 +180,8 @@ void paint_calc_redraw_planes(float planes[4][4],
void projectf(struct bglMats *mats, const float v[3], float p[2]);
float paint_calc_object_space_radius(struct ViewContext *vc, const float center[3], float pixel_radius);
float paint_get_tex_pixel(struct Brush *br, float u, float v, struct ImagePool *pool);
float paint_get_tex_pixel(struct MTex *mtex, float u, float v, struct ImagePool *pool);
void paint_get_tex_pixel_col(struct MTex *mtex, float u, float v, float rgba[4], struct ImagePool *pool);
int imapaint_pick_face(struct ViewContext *vc, const int mval[2], unsigned int *index, unsigned int totface);
void imapaint_pick_uv(struct Scene *scene, struct Object *ob, unsigned int faceindex, const int xy[2], float uv[2]);
void brush_drawcursor_texpaint_uvsculpt(struct bContext *C, int x, int y, void *customdata);
@ -204,7 +206,7 @@ int facemask_paint_poll(struct bContext *C);
typedef enum BrushStrokeMode {
BRUSH_STROKE_NORMAL,
BRUSH_STROKE_INVERT,
BRUSH_STROKE_SMOOTH,
BRUSH_STROKE_SMOOTH
} BrushStrokeMode;
/* paint_undo.c */

@ -28,6 +28,7 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BLI_math_vector.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@ -307,11 +308,7 @@ static int brush_select_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
if (ob) {
/* select current paint mode */
paint_mode = ob->mode &
(OB_MODE_SCULPT |
OB_MODE_VERTEX_PAINT |
OB_MODE_WEIGHT_PAINT |
OB_MODE_TEXTURE_PAINT);
paint_mode = ob->mode & OB_MODE_ALL_PAINT;
}
else {
return OPERATOR_CANCELLED;
@ -448,6 +445,158 @@ static void BRUSH_OT_uv_sculpt_tool_set(wmOperatorType *ot)
ot->prop = RNA_def_enum(ot->srna, "tool", uv_sculpt_tool_items, 0, "Tool", "");
}
/***** Stencil Control *****/
enum {
STENCIL_TRANSLATE,
STENCIL_SCALE,
STENCIL_ROTATE
} StencilControlMode;
typedef struct {
int init_mouse[2];
int init_spos[2];
int init_sdim[2];
float init_rot;
float init_angle;
float lenorig;
int mode;
Brush *br;
} StencilControlData;
static int stencil_control_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Paint *paint = paint_get_active_from_context(C);
Brush *br = paint->brush;
int mdiff[2];
StencilControlData *scd = MEM_mallocN(sizeof(StencilControlData), "stencil_control");
copy_v2_v2_int(scd->init_mouse, event->mval);
copy_v2_v2_int(scd->init_sdim, br->stencil_dimension);
copy_v2_v2_int(scd->init_spos, br->stencil_pos);
sub_v2_v2v2_int(mdiff, event->mval, br->stencil_pos);
scd->lenorig = sqrtf(mdiff[0]*mdiff[0] + mdiff[1]*mdiff[1]);
scd->br = br;
scd->init_rot = br->mtex.rot;
scd->init_angle = atan2(mdiff[1], mdiff[0]);
scd->mode = RNA_enum_get(op->ptr, "mode");
op->customdata = scd;
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int stencil_control_cancel(bContext *UNUSED(C), wmOperator *op)
{
StencilControlData *scd = op->customdata;
Brush *br = scd->br;
copy_v2_v2_int(br->stencil_dimension, scd->init_sdim);
copy_v2_v2_int(br->stencil_pos, scd->init_spos);
br->mtex.rot = scd->init_rot;
MEM_freeN(op->customdata);
return OPERATOR_CANCELLED;
}
static int stencil_control_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
StencilControlData *scd = op->customdata;
switch (event->type) {
case MOUSEMOVE:
{
int mdiff[2];
switch (scd->mode) {
case STENCIL_TRANSLATE:
sub_v2_v2v2_int(mdiff, event->mval, scd->init_mouse);
add_v2_v2v2_int(scd->br->stencil_pos, scd->init_spos,
mdiff);
break;
case STENCIL_SCALE:
{
float len, factor;
sub_v2_v2v2_int(mdiff, event->mval, scd->br->stencil_pos);
len = sqrtf(mdiff[0]*mdiff[0] + mdiff[1]*mdiff[1]);
factor = len/scd->lenorig;
mdiff[0] = factor*scd->init_sdim[0];
mdiff[1] = factor*scd->init_sdim[1];
copy_v2_v2_int(scd->br->stencil_dimension, mdiff);
break;
}
case STENCIL_ROTATE:
{
float angle;
sub_v2_v2v2_int(mdiff, event->mval, scd->br->stencil_pos);
angle = atan2(mdiff[1], mdiff[0]);
angle = scd->init_rot + angle - scd->init_angle;
if (angle < 0)
angle += 2*M_PI;
if (angle > 2*M_PI)
angle -= 2*M_PI;
scd->br->mtex.rot = angle;
break;
}
}
}
break;
case LEFTMOUSE:
if(event->val == KM_PRESS) {
MEM_freeN(op->customdata);
return OPERATOR_FINISHED;
}
case RIGHTMOUSE:
if(event->val == KM_PRESS) {
return stencil_control_cancel(C, op);
}
default:
break;
}
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_RUNNING_MODAL;
}
static void BRUSH_OT_stencil_control(wmOperatorType *ot)
{
static EnumPropertyItem stencil_control_items[] = {
{STENCIL_TRANSLATE, "TRANSLATION", 0, "Transation", ""},
{STENCIL_SCALE, "SCALE", 0, "Scale", ""},
{STENCIL_ROTATE, "ROTATION", 0, "Rotation", ""},
{0, NULL, 0, NULL, NULL}
};
/* identifiers */
ot->name = "Stencil Brush Control";
ot->description = "Control the stencil brush";
ot->idname = "BRUSH_OT_stencil_control";
/* api callbacks */
ot->invoke = stencil_control_invoke;
ot->modal = stencil_control_modal;
ot->cancel = stencil_control_cancel;
/* flags */
ot->flag = 0;
RNA_def_enum(ot->srna, "mode", stencil_control_items, 0, "Tool", "");
}
static void ed_keymap_stencil(wmKeyMap *keymap)
{
wmKeyMapItem *kmi;
kmi = WM_keymap_add_item(keymap, "BRUSH_OT_stencil_control", QKEY, KM_PRESS, 0, 0);
RNA_enum_set(kmi->ptr, "mode", STENCIL_TRANSLATE);
kmi = WM_keymap_add_item(keymap, "BRUSH_OT_stencil_control", QKEY, KM_PRESS, KM_SHIFT, 0);
RNA_enum_set(kmi->ptr, "mode", STENCIL_SCALE);
kmi = WM_keymap_add_item(keymap, "BRUSH_OT_stencil_control", QKEY, KM_PRESS, KM_CTRL, 0);
RNA_enum_set(kmi->ptr, "mode", STENCIL_ROTATE);
}
/**************************** registration **********************************/
void ED_operatortypes_paint(void)
@ -457,6 +606,7 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(BRUSH_OT_scale_size);
WM_operatortype_append(BRUSH_OT_curve_preset);
WM_operatortype_append(BRUSH_OT_reset);
WM_operatortype_append(BRUSH_OT_stencil_control);
/* note, particle uses a different system, can be added with existing operators in wm.py */
WM_operatortype_append(PAINT_OT_brush_select);
@ -671,6 +821,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_size(keymap, "tool_settings.sculpt.brush.size");
ed_keymap_paint_brush_radial_control(keymap, "sculpt", RC_ROTATION);
ed_keymap_stencil(keymap);
keymap_brush_select(keymap, OB_MODE_SCULPT, SCULPT_TOOL_DRAW, DKEY, 0);
keymap_brush_select(keymap, OB_MODE_SCULPT, SCULPT_TOOL_SMOOTH, SKEY, 0);
keymap_brush_select(keymap, OB_MODE_SCULPT, SCULPT_TOOL_PINCH, PKEY, 0);
@ -687,7 +839,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
/* */
kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.stroke_method");
RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.sculpt_stroke_method");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", SKEY, KM_PRESS, KM_SHIFT, 0);
RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_smooth_stroke");
@ -709,6 +861,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_size(keymap, "tool_settings.vertex_paint.brush.size");
ed_keymap_paint_brush_radial_control(keymap, "vertex_paint", RC_COLOR | RC_ROTATION);
ed_keymap_stencil(keymap);
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */
RNA_string_set(kmi->ptr, "data_path", "vertex_paint_object.data.use_paint_mask");
@ -718,6 +872,9 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", RKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.texture_angle_source_random");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.stroke_method");
/* Weight Paint mode */
keymap = WM_keymap_find(keyconf, "Weight Paint", 0, 0);
keymap->poll = weight_paint_mode_poll;
@ -738,6 +895,11 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_size(keymap, "tool_settings.weight_paint.brush.size");
ed_keymap_paint_brush_radial_control(keymap, "weight_paint", 0);
ed_keymap_stencil(keymap);
kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.stroke_method");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* face mask toggle */
RNA_string_set(kmi->ptr, "data_path", "weight_paint_object.data.use_paint_mask");
@ -776,6 +938,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_size(keymap, "tool_settings.image_paint.brush.size");
ed_keymap_paint_brush_radial_control(keymap, "image_paint", RC_COLOR | RC_ZOOM | RC_ROTATION);
ed_keymap_stencil(keymap);
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */
RNA_string_set(kmi->ptr, "data_path", "image_paint_object.data.use_paint_mask");
@ -785,6 +949,9 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", RKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path", "tool_settings.image_paint.brush.texture_angle_source_random");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path", "tool_settings.image_paint.brush.stroke_method");
/* face-mask mode */
keymap = WM_keymap_find(keyconf, "Face Mask", 0, 0);
keymap->poll = facemask_paint_poll;

@ -59,6 +59,7 @@
#include "BIF_glutil.h"
#include "RE_shader_ext.h"
#include "RE_render_ext.h"
#include "ED_view3d.h"
#include "ED_screen.h"
@ -176,13 +177,13 @@ float paint_calc_object_space_radius(ViewContext *vc, const float center[3],
return len_v3(delta) / scale;
}
float paint_get_tex_pixel(Brush *br, float u, float v, struct ImagePool *pool)
float paint_get_tex_pixel(MTex *mtex, float u, float v, struct ImagePool *pool)
{
TexResult texres = {0};
float co[3] = {u, v, 0.0f};
int hasrgb;
hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 0, &texres, pool);
hasrgb = multitex_ext(mtex->tex, co, NULL, NULL, 0, &texres, pool);
if (hasrgb & TEX_RGB)
texres.tin = rgb_to_grayscale(&texres.tr) * texres.ta;
@ -190,6 +191,23 @@ float paint_get_tex_pixel(Brush *br, float u, float v, struct ImagePool *pool)
return texres.tin;
}
void paint_get_tex_pixel_col(MTex *mtex, float u, float v, float rgba[4], struct ImagePool *pool)
{
float co[3] = {u, v, 0.0f};
int hasrgb;
float intensity;
hasrgb = externtex(mtex, co, &intensity,
rgba, rgba + 1, rgba + 2, rgba + 3, 0, pool);
if (!hasrgb) {
rgba[0] = intensity;
rgba[1] = intensity;
rgba[2] = intensity;
rgba[3] = 1.0f;
}
}
/* 3D Paint */
static void imapaint_project(Object *ob, float model[4][4], float proj[4][4], const float co[3], float pco[4])

@ -866,6 +866,7 @@ static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co
{
float delta[2];
float dist_squared;
float factor = 1.0;
sub_v2_v2v2(delta, mval, co_ss);
dist_squared = dot_v2v2(delta, delta); /* len squared */
@ -880,8 +881,9 @@ static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co
const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */
BKE_brush_sample_tex_3D(vc->scene, brush, co_ss_3d, rgba, 0, NULL);
}
factor = rgba[3];
}
return BKE_brush_curve_strength_clamp(brush, dist, brush_size_pressure);
return factor*BKE_brush_curve_strength_clamp(brush, dist, brush_size_pressure);
}
}
if (rgba)

@ -968,7 +968,7 @@ static float tex_strength(SculptSession *ss, Brush *br,
x += br->mtex.ofs[0];
y += br->mtex.ofs[1];
avg = paint_get_tex_pixel(br, x, y, ss->tex_pool);
avg = paint_get_tex_pixel(&br->mtex, x, y, ss->tex_pool);
avg += br->texture_sample_bias;
}

@ -105,6 +105,9 @@ typedef struct Brush {
float add_col[3];
float sub_col[3];
int stencil_pos[2];
int stencil_dimension[2];
} Brush;
/* Brush.flag */

@ -500,6 +500,7 @@ typedef struct ColorMapping {
#define MTEX_MAP_MODE_3D 2
#define MTEX_MAP_MODE_AREA 3
#define MTEX_MAP_MODE_RANDOM 4
#define MTEX_MAP_MODE_STENCIL 5
/* **************** EnvMap ********************* */

@ -433,6 +433,7 @@ static void rna_def_brush_texture_slot(BlenderRNA *brna)
{MTEX_MAP_MODE_TILED, "TILED", 0, "Tiled", ""},
{MTEX_MAP_MODE_3D, "3D", 0, "3D", ""},
{MTEX_MAP_MODE_RANDOM, "RANDOM", 0, "Random", ""},
{MTEX_MAP_MODE_STENCIL, "STENCIL", 0, "Stencil", ""},
{0, NULL, 0, NULL, NULL}
};
@ -441,6 +442,7 @@ static void rna_def_brush_texture_slot(BlenderRNA *brna)
{MTEX_MAP_MODE_TILED, "TILED", 0, "Tiled", ""},
{MTEX_MAP_MODE_3D, "3D", 0, "3D", ""},
{MTEX_MAP_MODE_RANDOM, "RANDOM", 0, "Random", ""},
{MTEX_MAP_MODE_STENCIL, "STENCIL", 0, "Stencil", ""},
{0, NULL, 0, NULL, NULL}
};