Texture sampling function refactoring:

ALERT! POSSIBLE BREAKING COMMIT, ESPECIALLY FOR SCULPT!

Separate the sculpt sampling function so that it can be reused
from other paint systems. This includes updating of the relevant
coordinates for anchored and rake style brushes, which are now
being updated as part of the stroke system.

I left only code for area-style brush texture mapping in sculpt
code, since it requires a few data structures not present on other
paint systems.

This commit makes it almost as easy to support rake on other systems as
exposing the python UI for it. Also it makes it totally possible to
have texture painting capabilities in vertex paint too :) These commits
will follow very soon.

Also, even if I did my best to keep the code from breaking, (even fixed a
leftover bug from coordinate changes) this is a big change. Please test!
This commit is contained in:
Antony Riakiotakis 2013-03-13 03:46:22 +00:00
parent 153b63e0fd
commit acd3bef34e
10 changed files with 329 additions and 211 deletions

@ -70,7 +70,9 @@ float BKE_brush_curve_strength(struct Brush *br, float p, const float len); /* u
/* sampling */
void BKE_brush_sample_tex(const struct Scene *scene, struct Brush *brush, const float sampleco[3], float rgba[4], const int thread, struct ImagePool *pool);
void BKE_brush_sample_tex_2D(const struct Scene *scene, struct Brush *brush, const float xy[2], float rgba[4], const int thread);
float BKE_brush_sample_tex_3D(const Scene *scene, struct Brush *br,const float point[3],
float rgba[3], struct ImagePool *pool);
float BKE_brush_sample_tex_2D(const struct Scene *scene, struct Brush *brush, const float xy[2], float rgba[4], struct ImagePool *pool);
void BKE_brush_imbuf_new(const struct Scene *scene, struct Brush *brush, short flt, short texfalloff, int size,
struct ImBuf **imbuf, int use_color_correction);

@ -48,6 +48,7 @@ struct PBVH;
struct Scene;
struct StrokeCache;
struct ImagePool;
struct UnifiedPaintSettings;
extern const char PAINT_CURSOR_SCULPT[3];
extern const char PAINT_CURSOR_VERTEX_PAINT[3];
@ -90,7 +91,7 @@ int paint_is_bmesh_face_hidden(struct BMFace *f);
/* paint masks */
float paint_grid_paint_mask(const struct GridPaintMask *gpm, unsigned level,
unsigned x, unsigned y);
void paint_calculate_rake_rotation(struct UnifiedPaintSettings *ups, const float mouse_pos[2]);
/* Session data (mode-specific) */
typedef struct SculptSession {

@ -513,8 +513,98 @@ void BKE_brush_sample_tex(const Scene *scene, Brush *brush, const float sampleco
}
}
/* Return a multiplier for brush strength on a particular vertex. */
float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br,
const float point[3],
float rgba[3],
struct ImagePool *pool)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
MTex *mtex = &br->mtex;
float intensity = 1.0;
bool hasrgb = false;
if (!mtex->tex) {
intensity = 1;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
/* Get strength by feeding the vertex
* location directly into a texture */
hasrgb = externtex(mtex, point, &intensity,
rgba, rgba + 1, rgba + 2, rgba + 3, 0, 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 co[2];
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
/* keep coordinates relative to mouse */
rotation += ups->brush_rotation;
point_2d[0] -= ups->tex_mouse[0];
point_2d[1] -= ups->tex_mouse[1];
/* use pressure adjusted size for fixed mode */
radius = ups->pixel_radius;
x = point_2d[0];
y = point_2d[1];
}
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);
x = point_2d[0];
y = point_2d[1];
}
x /= radius;
y /= radius;
/* it is probably worth optimizing for those cases where
* the texture is not rotated by skipping the calls to
* atan2, sqrtf, sin, and cos. */
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);
}
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, 0, pool);
}
intensity += br->texture_sample_bias;
if (!hasrgb) {
rgba[0] = intensity;
rgba[1] = intensity;
rgba[2] = intensity;
rgba[3] = 1.0f;
}
return intensity;
}
/* Brush Sampling for 2D brushes. when we unify the brush systems this will be necessarily a separate function */
void BKE_brush_sample_tex_2D(const Scene *scene, Brush *brush, const float xy[2], float rgba[4], const int thread)
float BKE_brush_sample_tex_2D(const Scene *scene, Brush *brush, const float xy[2], float rgba[4], struct ImagePool *pool)
{
MTex *mtex = &brush->mtex;
@ -527,7 +617,7 @@ void BKE_brush_sample_tex_2D(const Scene *scene, Brush *brush, const float xy[2]
co[1] = xy[1] / radius;
co[2] = 0.0f;
hasrgb = externtex(mtex, co, &tin, &tr, &tg, &tb, &ta, thread, NULL);
hasrgb = externtex(mtex, co, &tin, &tr, &tg, &tb, &ta, 0, pool);
if (hasrgb) {
rgba[0] = tr;
@ -541,9 +631,11 @@ void BKE_brush_sample_tex_2D(const Scene *scene, Brush *brush, const float xy[2]
rgba[2] = tin;
rgba[3] = 1.0f;
}
return tin;
}
else {
rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0f;
return 1.0;
}
}

@ -40,6 +40,7 @@
#include "BLI_bitmap.h"
#include "BLI_utildefines.h"
#include "BLI_math_vector.h"
#include "BKE_brush.h"
#include "BKE_context.h"
@ -300,3 +301,22 @@ float paint_grid_paint_mask(const GridPaintMask *gpm, unsigned level,
return gpm->data[(y * factor) * gridsize + (x * factor)];
}
/* threshhold to move before updating the brush rotation */
#define RAKE_THRESHHOLD 20
void paint_calculate_rake_rotation(UnifiedPaintSettings *ups, const float mouse_pos[2])
{
const float u = 0.5f;
const float r = RAKE_THRESHHOLD;
float dpos[2];
sub_v2_v2v2(dpos, ups->last_rake, mouse_pos);
if (len_squared_v2(dpos) >= r * r) {
ups->brush_rotation = atan2(dpos[0], dpos[1]);
interp_v2_v2v2(ups->last_rake, ups->last_rake,
mouse_pos, u);
}
}

@ -207,14 +207,14 @@ static int load_tex(Brush *br, ViewContext *vc)
x = (float)i / size;
y = (float)j / size;
x -= 0.5f;
y -= 0.5f;
if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) {
x *= vc->ar->winx / radius;
y *= vc->ar->winy / radius;
}
else {
x -= 0.5f;
y -= 0.5f;
x *= 2;
y *= 2;
}
@ -420,8 +420,7 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush,
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) {
/* brush rotation */
glTranslatef(0.5, 0.5, 0);
glRotatef((double)RAD2DEGF((brush->flag & BRUSH_RAKE) ?
ups->last_angle : ups->special_rotation),
glRotatef((double)RAD2DEGF(ups->brush_rotation),
0.0, 0.0, 1.0);
glTranslatef(-0.5f, -0.5f, 0);
@ -434,11 +433,10 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush,
if (ups->draw_anchored) {
const float *aim = ups->anchored_initial_mouse;
const rcti *win = &vc->ar->winrct;
quad.xmin = aim[0] - ups->anchored_size - win->xmin;
quad.ymin = aim[1] - ups->anchored_size - win->ymin;
quad.xmax = aim[0] + ups->anchored_size - win->xmin;
quad.ymax = aim[1] + ups->anchored_size - win->ymin;
quad.xmin = aim[0] - ups->anchored_size;
quad.ymin = aim[1] - ups->anchored_size;
quad.xmax = aim[0] + ups->anchored_size;
quad.ymax = aim[1] + ups->anchored_size;
}
else {
const int radius = BKE_brush_size_get(vc->scene, brush);
@ -537,38 +535,22 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
* mouse over too, not just during a stroke */
view3d_set_viewcontext(C, &vc);
if (brush->flag & BRUSH_RAKE)
/* here, translation contains the mouse coordinates. */
paint_calculate_rake_rotation(ups, translation);
/* draw overlay */
paint_draw_alpha_overlay(ups, brush, &vc, x, y);
/* TODO: as sculpt and other paint modes are unified, this
* special mode of drawing will go away */
if (vc.obact->sculpt) {
float location[3];
int pixel_radius, hit;
/* this is probably here so that rake takes into
* account the brush movements before the stroke
* starts, but this doesn't really belong in draw code
* TODO) */
{
const float u = 0.5f;
const float v = 1 - u;
const float r = 20;
const float dx = ups->last_x - x;
const float dy = ups->last_y - y;
if (dx * dx + dy * dy >= r * r) {
ups->last_angle = atan2(dx, dy);
ups->last_x = u * ups->last_x + v * x;
ups->last_y = u * ups->last_y + v * y;
}
}
/* test if brush is over the mesh */
hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location);
/* draw overlay */
paint_draw_alpha_overlay(ups, brush, &vc, x, y);
if (BKE_brush_use_locked_size(scene, brush))
BKE_brush_size_set(scene, brush, pixel_radius);

@ -234,7 +234,7 @@ static void brush_painter_2d_do_partial(BrushPainter *painter, ImBuf *oldtexibuf
xy[0] = x + xoff;
xy[1] = y + yoff;
BKE_brush_sample_tex_2D(scene, brush, xy, tf, 0);
BKE_brush_sample_tex_2D(scene, brush, xy, tf, NULL);
}
bf[0] = tf[0] * mf[0];
@ -265,7 +265,7 @@ static void brush_painter_2d_do_partial(BrushPainter *painter, ImBuf *oldtexibuf
xy[0] = x + xoff;
xy[1] = y + yoff;
BKE_brush_sample_tex_2D(scene, brush, xy, rgba, 0);
BKE_brush_sample_tex_2D(scene, brush, xy, rgba, NULL);
rgba_float_to_uchar(t, rgba);
}

@ -64,8 +64,9 @@ struct PaintStroke *paint_stroke_new(struct bContext *C,
StrokeUpdateStep update_step, StrokeDone done, int event_type);
void paint_stroke_data_free(struct wmOperator *op);
bool paint_space_stroke_enabled(struct Brush *br);
bool paint_supports_dynamic_size(struct Brush *br);
bool paint_space_stroke_enabled(struct Brush *br, enum PaintMode mode);
bool paint_supports_dynamic_size(struct Brush *br, enum PaintMode mode);
bool paint_supports_moving_texture(struct Brush *br, enum PaintMode mode);
bool paint_supports_jitter(enum PaintMode mode);
struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf);

@ -35,6 +35,7 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLI_rand.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@ -91,6 +92,10 @@ typedef struct PaintStroke {
/* event that started stroke, for modal() return */
int event_type;
bool brush_init;
float initial_mouse[2];
float cached_pressure;
StrokeGetLocation get_location;
StrokeTestStart test_start;
StrokeUpdateStep update_step;
@ -136,6 +141,98 @@ static float event_tablet_data(wmEvent *event, int *pen_flip)
return pressure;
}
#if 1
/* Initialize the stroke cache variants from operator properties */
static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode,
struct PaintStroke *stroke,
float mouse[2], float pressure)
{
Scene *scene = CTX_data_scene(C);
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
/* XXX: Use pressure value from first brush step for brushes which don't
* support strokes (grab, thumb). They depends on initial state and
* brush coord/pressure/etc.
* It's more an events design issue, which doesn't split coordinate/pressure/angle
* changing events. We should avoid this after events system re-design */
if (paint_supports_dynamic_size(brush, mode) || !stroke->brush_init) {
copy_v2_v2(stroke->initial_mouse, mouse);
copy_v2_v2(ups->tex_mouse, mouse);
stroke->cached_pressure = pressure;
}
/* Truly temporary data that isn't stored in properties */
ups->draw_pressure = TRUE;
ups->pressure_value = stroke->cached_pressure;
ups->pixel_radius = BKE_brush_size_get(scene, brush);
if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, mode)) {
ups->pixel_radius *= stroke->cached_pressure;
}
if (!(brush->flag & BRUSH_ANCHORED ||
ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)))
{
copy_v2_v2(ups->tex_mouse, mouse);
if ((brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) &&
(brush->flag & BRUSH_RANDOM_ROTATION) &&
!(brush->flag & BRUSH_RAKE))
{
ups->brush_rotation = 2.0f * (float)M_PI * BLI_frand();
}
}
if (brush->flag & BRUSH_ANCHORED) {
bool hit = false;
float halfway[2];
const float dx = mouse[0] - stroke->initial_mouse[0];
const float dy = mouse[1] - stroke->initial_mouse[1];
ups->anchored_size = ups->pixel_radius = sqrt(dx * dx + dy * dy);
ups->brush_rotation = atan2(dx, dy) + M_PI;
if (brush->flag & BRUSH_EDGE_TO_EDGE) {
float out[3];
halfway[0] = dx * 0.5f + stroke->initial_mouse[0];
halfway[1] = dy * 0.5f + stroke->initial_mouse[1];
if (stroke->get_location) {
if(stroke->get_location(C, out, halfway)) {
hit = true;
}
} else {
hit = true;
}
}
if(hit) {
copy_v2_v2(ups->anchored_initial_mouse, halfway);
copy_v2_v2(ups->tex_mouse, halfway);
ups->anchored_size /= 2.0f;
ups->pixel_radius /= 2.0f;
}
else
copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse);
ups->draw_anchored = 1;
}
else if (brush->flag & BRUSH_RAKE) {
if (!stroke->brush_init)
copy_v2_v2(ups->last_rake, mouse);
else
paint_calculate_rake_rotation(ups, mouse);
}
stroke->brush_init = TRUE;
}
#endif
/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, const float mouse_in[2])
{
@ -199,6 +296,8 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev
else
zero_v3(location);
paint_brush_update(C, brush, mode, stroke, mouse_out, pressure);
/* Add to stroke */
RNA_collection_add(op->ptr, "stroke", &itemptr);
@ -247,9 +346,11 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2],
static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const float final_mouse[2])
{
PaintStroke *stroke = op->customdata;
PaintMode mode = paintmode_get_active_from_context(C);
int cnt = 0;
if (paint_space_stroke_enabled(stroke->brush)) {
if (paint_space_stroke_enabled(stroke->brush, mode)) {
float mouse[2];
float vec[2];
float length, scale;
@ -355,16 +456,41 @@ static void stroke_done(struct bContext *C, struct wmOperator *op)
}
/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
bool paint_space_stroke_enabled(Brush *br)
bool paint_space_stroke_enabled(Brush *br, PaintMode mode)
{
return (br->flag & BRUSH_SPACE) && paint_supports_dynamic_size(br);
return (br->flag & BRUSH_SPACE) && paint_supports_dynamic_size(br, mode);
}
/* return true if the brush size can change during paint (normally used for pressure) */
bool paint_supports_dynamic_size(Brush *br)
bool paint_supports_dynamic_size(Brush *br, PaintMode mode)
{
return !(br->flag & BRUSH_ANCHORED) &&
!ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK);
if(br->flag & BRUSH_ANCHORED)
return false;
switch(mode) {
case PAINT_SCULPT:
if(ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK))
return false;
default:
;
}
return true;
}
/* return true if the brush size can change during paint (normally used for pressure) */
bool paint_supports_moving_texture(Brush *br, PaintMode mode)
{
if(br->flag & BRUSH_ANCHORED)
return false;
switch(mode) {
case PAINT_SCULPT:
if(ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK))
return false;
default:
;
}
return true;
}
bool paint_supports_jitter(PaintMode mode)
@ -439,6 +565,7 @@ static void paint_stroke_sample_average(const PaintStroke *stroke,
int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
{
Paint *p = paint_get_active_from_context(C);
PaintMode mode = paintmode_get_active_from_context(C);
PaintStroke *stroke = op->customdata;
PaintSample sample_average;
float mouse[2];
@ -489,7 +616,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
{
if (stroke->stroke_started) {
if (paint_smooth_stroke(stroke, mouse, &sample_average)) {
if (paint_space_stroke_enabled(stroke->brush)) {
if (paint_space_stroke_enabled(stroke->brush, mode)) {
if (!paint_space_stroke(C, op, event, mouse)) {
//ED_region_tag_redraw(ar);
}
@ -508,7 +635,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
* instead of waiting till we have moved the space distance */
if (first &&
stroke->stroke_started &&
paint_space_stroke_enabled(stroke->brush) &&
paint_space_stroke_enabled(stroke->brush, mode) &&
!(stroke->brush->flag & BRUSH_ANCHORED) &&
!(stroke->brush->flag & BRUSH_SMOOTH_STROKE))
{

@ -245,7 +245,6 @@ typedef struct StrokeCache {
float pressure;
float mouse[2];
float bstrength;
float tex_mouse[2];
/* The rest is temporary storage that isn't saved as a property */
@ -259,8 +258,8 @@ typedef struct StrokeCache {
Brush *brush;
float (*face_norms)[3]; /* Copy of the mesh faces' normals */
float special_rotation; /* Texture rotation (radians) for anchored and rake modes */
int pixel_radius, previous_pixel_radius;
float special_rotation;
float grab_delta[3], grab_delta_symmetry[3];
float old_grab_location[3], orig_grab_location[3];
@ -284,8 +283,8 @@ typedef struct StrokeCache {
int radial_symmetry_pass;
float symm_rot_mat[4][4];
float symm_rot_mat_inv[4][4];
float last_rake[2]; /* Last location of updating rake rotation */
int original;
float anchored_location[3];
float vertex_rotation;
@ -924,25 +923,22 @@ static float tex_strength(SculptSession *ss, Brush *br,
const float fno[3],
const float mask)
{
const Scene *scene = ss->cache->vc->scene;
MTex *mtex = &br->mtex;
float avg = 1;
float rgba[4];
if (!mtex->tex) {
avg = 1;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
float jnk;
/* Get strength by feeding the vertex
* location directly into a texture */
externtex(mtex, point, &avg,
&jnk, &jnk, &jnk, &jnk, 0, ss->tex_pool);
avg = BKE_brush_sample_tex_3D(scene, br, point, rgba, ss->tex_pool);
}
else if (ss->texcache) {
float rotation = -mtex->rot;
float symm_point[3], point_2d[2];
float x = 0.0f, y = 0.0f; /* Quite warnings */
float radius = 1.0f; /* Quite warnings */
/* if the active area is being applied for symmetry, flip it
* across the symmetry axis and rotate it back to the original
@ -956,77 +952,31 @@ static float tex_strength(SculptSession *ss, Brush *br,
ED_view3d_project_float_v2_m4(ss->cache->vc->ar, symm_point, point_2d, ss->cache->projection_mat);
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
/* keep coordinates relative to mouse */
rotation += ss->cache->special_rotation;
point_2d[0] -= ss->cache->tex_mouse[0];
point_2d[1] -= ss->cache->tex_mouse[1];
/* use pressure adjusted size for fixed mode */
radius = ss->cache->pixel_radius;
x = point_2d[0];
y = point_2d[1];
}
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(ss->cache->vc->scene, br);
x = point_2d[0];
y = point_2d[1];
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_AREA) {
/* still no symmetry supported for other paint modes.
* Sculpt does it DIY */
if (mtex->brush_map_mode == MTEX_MAP_MODE_AREA) {
/* Similar to fixed mode, but projects from brush angle
* rather than view direction */
/* Rotation is handled by the brush_local_mat */
rotation = 0;
mul_m4_v3(ss->cache->brush_local_mat, symm_point);
x = symm_point[0];
y = symm_point[1];
x *= br->mtex.size[0];
y *= br->mtex.size[1];
x += br->mtex.ofs[0];
y += br->mtex.ofs[1];
avg = paint_get_tex_pixel(br, x, y, ss->tex_pool);
avg += br->texture_sample_bias;
} else {
avg = BKE_brush_sample_tex_3D(scene, br, point_2d, rgba, ss->tex_pool);
}
if (mtex->brush_map_mode != MTEX_MAP_MODE_AREA) {
x /= ss->cache->vc->ar->winx;
y /= ss->cache->vc->ar->winy;
if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
x -= 0.5f;
y -= 0.5f;
}
x *= ss->cache->vc->ar->winx / radius;
y *= ss->cache->vc->ar->winy / radius;
}
/* it is probably worth optimizing for those cases where
* the texture is not rotated by skipping the calls to
* atan2, sqrtf, sin, and cos. */
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);
}
x *= br->mtex.size[0];
y *= br->mtex.size[1];
x += br->mtex.ofs[0];
y += br->mtex.ofs[1];
avg = paint_get_tex_pixel(br, x, y, ss->tex_pool);
}
avg += br->texture_sample_bias;
/* Falloff curve */
avg *= BKE_brush_curve_strength(br, len, ss->cache->radius);
@ -3716,21 +3666,21 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio
ss->cache = cache;
/* Set scaling adjustment */
ss->cache->scale[0] = 1.0f / ob->size[0];
ss->cache->scale[1] = 1.0f / ob->size[1];
ss->cache->scale[2] = 1.0f / ob->size[2];
cache->scale[0] = 1.0f / ob->size[0];
cache->scale[1] = 1.0f / ob->size[1];
cache->scale[2] = 1.0f / ob->size[2];
ss->cache->plane_trim_squared = brush->plane_trim * brush->plane_trim;
cache->plane_trim_squared = brush->plane_trim * brush->plane_trim;
ss->cache->flag = 0;
cache->flag = 0;
sculpt_init_mirror_clipping(ob, ss);
/* Initial mouse location */
if (mouse)
copy_v2_v2(ss->cache->initial_mouse, mouse);
copy_v2_v2(cache->initial_mouse, mouse);
else
zero_v2(ss->cache->initial_mouse);
zero_v2(cache->initial_mouse);
mode = RNA_enum_get(op->ptr, "mode");
cache->invert = mode == BRUSH_STROKE_INVERT;
@ -3742,7 +3692,7 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio
else brush->flag &= ~BRUSH_INVERTED;
/* Alt-Smooth */
if (ss->cache->alt_smooth) {
if (cache->alt_smooth) {
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
cache->saved_mask_brush_tool = brush->mask_tool;
brush->mask_tool = BRUSH_MASK_SMOOTH;
@ -3763,7 +3713,7 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio
}
copy_v2_v2(cache->mouse, cache->initial_mouse);
copy_v2_v2(cache->tex_mouse, cache->initial_mouse);
copy_v2_v2(ups->tex_mouse, cache->initial_mouse);
/* Truly temporary data that isn't stored in properties */
@ -3818,8 +3768,6 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio
}
}
cache->special_rotation = (brush->flag & BRUSH_RAKE) ? ups->last_angle : 0;
cache->first_time = 1;
cache->vertex_rotation = 0;
@ -3890,9 +3838,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
copy_v3_v3(cache->old_grab_location, grab_location);
if (tool == SCULPT_TOOL_GRAB)
copy_v3_v3(ups->anchored_location, cache->true_location);
copy_v3_v3(cache->anchored_location, cache->true_location);
else if (tool == SCULPT_TOOL_THUMB)
copy_v3_v3(ups->anchored_location, cache->orig_grab_location);
copy_v3_v3(cache->anchored_location, cache->orig_grab_location);
if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) {
/* location stays the same for finding vertices in brush radius */
@ -3900,7 +3848,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
ups->draw_anchored = 1;
copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse);
ups->anchored_size = cache->pixel_radius;
ups->anchored_size = ups->pixel_radius;
}
}
}
@ -3935,18 +3883,11 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob,
* brush coord/pressure/etc.
* It's more an events design issue, which doesn't split coordinate/pressure/angle
* changing events. We should avoid this after events system re-design */
if (paint_supports_dynamic_size(brush) || cache->first_time) {
if (paint_supports_dynamic_size(brush, PAINT_SCULPT) || cache->first_time) {
cache->pressure = RNA_float_get(ptr, "pressure");
}
/* Truly temporary data that isn't stored in properties */
ups->draw_pressure = 1;
ups->pressure_value = cache->pressure;
cache->previous_pixel_radius = cache->pixel_radius;
cache->pixel_radius = BKE_brush_size_get(scene, brush);
if (cache->first_time) {
if (!BKE_brush_use_locked_size(scene, brush)) {
cache->initial_radius = paint_calc_object_space_radius(cache->vc,
@ -3959,8 +3900,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob,
}
}
if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush)) {
cache->pixel_radius *= cache->pressure;
if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, PAINT_SCULPT)) {
cache->radius = cache->initial_radius * cache->pressure;
}
else {
@ -3969,77 +3909,25 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob,
cache->radius_squared = cache->radius * cache->radius;
if (!(brush->flag & BRUSH_ANCHORED ||
ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)))
{
copy_v2_v2(cache->tex_mouse, cache->mouse);
if ((brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) &&
(brush->flag & BRUSH_RANDOM_ROTATION) &&
!(brush->flag & BRUSH_RAKE))
{
cache->special_rotation = 2.0f * (float)M_PI * BLI_frand();
}
}
if (brush->flag & BRUSH_ANCHORED) {
int hit = 0;
const float dx = cache->mouse[0] - cache->initial_mouse[0];
const float dy = cache->mouse[1] - cache->initial_mouse[1];
ups->anchored_size = cache->pixel_radius = sqrt(dx * dx + dy * dy);
cache->special_rotation = atan2(dx, dy) + M_PI;
if (brush->flag & BRUSH_EDGE_TO_EDGE) {
float halfway[2];
float out[3];
halfway[0] = dx * 0.5f + cache->initial_mouse[0];
halfway[1] = dy * 0.5f + cache->initial_mouse[1];
halfway[0] = 0.5f * (cache->mouse[0] + cache->initial_mouse[0]);
halfway[1] = 0.5f * (cache->mouse[1] + cache->initial_mouse[1]);
if (sculpt_stroke_get_location(C, out, halfway)) {
copy_v3_v3(ups->anchored_location, out);
copy_v2_v2(ups->anchored_initial_mouse, halfway);
copy_v2_v2(cache->tex_mouse, halfway);
copy_v3_v3(cache->true_location, ups->anchored_location);
ups->anchored_size /= 2.0f;
cache->pixel_radius /= 2.0f;
hit = 1;
copy_v3_v3(cache->anchored_location, out);
copy_v3_v3(cache->true_location, cache->anchored_location);
}
}
if (!hit)
copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse);
cache->radius = paint_calc_object_space_radius(paint_stroke_view_context(stroke),
cache->true_location,
cache->pixel_radius);
ups->pixel_radius);
cache->radius_squared = cache->radius * cache->radius;
copy_v3_v3(ups->anchored_location, cache->true_location);
ups->draw_anchored = 1;
}
else if (brush->flag & BRUSH_RAKE) {
const float u = 0.5f;
const float v = 1 - u;
const float r = 20;
const float dx = cache->last_rake[0] - cache->mouse[0];
const float dy = cache->last_rake[1] - cache->mouse[1];
if (cache->first_time) {
copy_v2_v2(cache->last_rake, cache->mouse);
}
else if (dx * dx + dy * dy >= r * r) {
cache->special_rotation = atan2(dx, dy);
cache->last_rake[0] = u * cache->last_rake[0] + v * cache->mouse[0];
cache->last_rake[1] = u * cache->last_rake[1] + v * cache->mouse[1];
}
copy_v3_v3(cache->anchored_location, cache->true_location);
}
sculpt_update_brush_delta(ups, ob, brush);
@ -4052,11 +3940,11 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob,
ups->draw_anchored = 1;
copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse);
copy_v3_v3(ups->anchored_location, cache->true_location);
ups->anchored_size = cache->pixel_radius;
copy_v3_v3(cache->anchored_location, cache->true_location);
ups->anchored_size = ups->pixel_radius;
}
ups->special_rotation = cache->special_rotation;
cache->special_rotation = ups->brush_rotation;
}
/* Returns true if any of the smoothing modes are active (currently
@ -4312,6 +4200,7 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op,
static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
{
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -4323,7 +4212,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
BKE_pbvh_bmesh_detail_size_set(ss->pbvh,
(ss->cache->radius /
(float)ss->cache->pixel_radius) *
(float)ups->pixel_radius) *
(float)sd->detail_size);
if (sculpt_stroke_dynamic_topology(ss, brush)) {
@ -4368,7 +4257,6 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
/* reset values used to draw brush after completing the stroke */
ups->draw_anchored = 0;
ups->draw_pressure = 0;
ups->special_rotation = 0;
/* Finished */
if (ss->cache) {

@ -888,20 +888,25 @@ typedef struct UnifiedPaintSettings {
/* rake rotation */
/* record movement of mouse so that rake can start at an intuitive angle */
float last_x, last_y;
float last_angle;
float last_rake[2];
int pad;
float special_rotation;
float brush_rotation;
// all this below is used to communicate with the cursor drawing routine
int draw_anchored;
int anchored_size;
float anchored_location[3];
float anchored_initial_mouse[2];
/* drawing pressure */
int draw_pressure;
float pressure_value;
/* position of mouse, used to sample the texture */
float tex_mouse[2];
/* radius of brush, premultiplied with pressure.
* In case of anchored brushes contains that radius */
float pixel_radius;
} UnifiedPaintSettings;
typedef enum {