diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-01-19 15:31:31 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-01-19 15:31:31 +0400 |
commit | 7b2101ace983cc9d7112dd45a8b264b51657dea9 (patch) | |
tree | c1d75f2ae3f30fe9de51c1c7332e862064a93193 /source/blender/editors/sculpt_paint | |
parent | 787ea5feb4a80babccd86689b066153ac57db91c (diff) | |
parent | a87c5eb52cd4951b138f518bf12d498bc5e5eb8a (diff) |
svn merge ^/trunk/blender -r43482:43524
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r-- | source/blender/editors/sculpt_paint/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_cursor.c | 607 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_intern.h | 2 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_stroke.c | 560 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 30 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 2 |
6 files changed, 629 insertions, 573 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 29e97c77bec..8a6a236d10f 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -39,6 +39,7 @@ set(INC_SYS ) set(SRC + paint_cursor.c paint_image.c paint_ops.c paint_stroke.c diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c new file mode 100644 index 00000000000..0148f11b930 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -0,0 +1,607 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2009 by Nicholas Bishop + * All rights reserved. + * + * Contributor(s): Jason Wilkins, Tom Musgrove. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/editors/sculpt_paint/paint_cursor.c + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_brush_types.h" +#include "DNA_color_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_paint.h" + +#include "WM_api.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "ED_view3d.h" + +#include "paint_intern.h" +/* still needed for sculpt_stroke_get_location, should be + removed eventually (TODO) */ +#include "sculpt_intern.h" + +/* TODOs: + + Some of the cursor drawing code is doing non-draw stuff + (e.g. updating the brush rake angle). This should be cleaned up + still. + + There is also some ugliness with sculpt-specific code. + */ + +typedef struct Snapshot { + float size[3]; + float ofs[3]; + float rot; + int brush_size; + int winx; + int winy; + int brush_map_mode; + int curve_changed_timestamp; +} Snapshot; + +static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +{ + MTex* mtex = &brush->mtex; + + return (((mtex->tex) && + equals_v3v3(mtex->ofs, snap->ofs) && + equals_v3v3(mtex->size, snap->size) && + mtex->rot == snap->rot) && + + /* make brush smaller shouldn't cause a resample */ + ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && + (brush_size(vc->scene, brush) <= snap->brush_size)) || + (brush_size(vc->scene, brush) == snap->brush_size)) && + + (mtex->brush_map_mode == snap->brush_map_mode) && + (vc->ar->winx == snap->winx) && + (vc->ar->winy == snap->winy)); +} + +static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +{ + if (brush->mtex.tex) { + snap->brush_map_mode = brush->mtex.brush_map_mode; + copy_v3_v3(snap->ofs, brush->mtex.ofs); + copy_v3_v3(snap->size, brush->mtex.size); + snap->rot = brush->mtex.rot; + } + else { + snap->brush_map_mode = -1; + snap->ofs[0]= snap->ofs[1]= snap->ofs[2]= -1; + snap->size[0]= snap->size[1]= snap->size[2]= -1; + snap->rot = -1; + } + + snap->brush_size = brush_size(vc->scene, brush); + snap->winx = vc->ar->winx; + snap->winy = vc->ar->winy; +} + +static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) +{ + static GLuint overlay_texture = 0; + static int init = 0; + static int tex_changed_timestamp = -1; + static int curve_changed_timestamp = -1; + static Snapshot snap; + static int old_size = -1; + + GLubyte* buffer = NULL; + + int size; + int j; + int refresh; + +#ifndef _OPENMP + (void)sd; /* quied unused warning */ +#endif + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0; + + refresh = + !overlay_texture || + (br->mtex.tex && + (!br->mtex.tex->preview || + br->mtex.tex->preview->changed_timestamp[0] != tex_changed_timestamp)) || + !br->curve || + br->curve->changed_timestamp != curve_changed_timestamp || + !same_snap(&snap, br, vc); + + if (refresh) { + if (br->mtex.tex && br->mtex.tex->preview) + tex_changed_timestamp = br->mtex.tex->preview->changed_timestamp[0]; + + if (br->curve) + curve_changed_timestamp = br->curve->changed_timestamp; + + make_snap(&snap, br, vc); + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + int s = brush_size(vc->scene, br); + int r = 1; + + for (s >>= 1; s > 0; s >>= 1) + r++; + + size = (1<<r); + + if (size < 256) + size = 256; + + if (size < old_size) + size = old_size; + } + else + size = 512; + + if (old_size != size) { + if (overlay_texture) { + glDeleteTextures(1, &overlay_texture); + overlay_texture = 0; + } + + init = 0; + + old_size = size; + } + + buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "load_tex"); + + #pragma omp parallel for schedule(static) if (sd->flags & SCULPT_USE_OPENMP) + for (j= 0; j < size; j++) { + int i; + float y; + float len; + + for (i= 0; i < size; i++) { + + // largely duplicated from tex_strength + + const float rotation = -br->mtex.rot; + float radius = brush_size(vc->scene, br); + int index = j*size + i; + float x; + float avg; + + 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 *= 2; + y *= 2; + } + + len = sqrtf(x*x + y*y); + + if ((br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) || 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. */ + if (br->mtex.tex && (rotation > 0.001f || rotation < -0.001f)) { + const float angle = atan2f(y, x) + rotation; + + x = len * cosf(angle); + y = len * sinf(angle); + } + + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; + + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; + + avg = br->mtex.tex ? paint_get_tex_pixel(br, x, y) : 1; + + avg += br->texture_sample_bias; + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) + avg *= brush_curve_strength(br, len, 1); /* Falloff curve */ + + buffer[index] = 255 - (GLubyte)(255*avg); + } + else { + buffer[index] = 0; + } + } + } + + if (!overlay_texture) + glGenTextures(1, &overlay_texture); + } + else { + size= old_size; + } + + 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); + init = 1; + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); + } + + if (buffer) + MEM_freeN(buffer); + } + + glEnable(GL_TEXTURE_2D); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + } + + return 1; +} + +static int project_brush_radius(ViewContext *vc, + float radius, + const float location[3]) +{ + float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; + + ED_view3d_global_to_vector(vc->rv3d, location, view); + + // create a vector that is not orthogonal to view + + if (fabsf(view[0]) < 0.1f) { + nonortho[0] = view[0] + 1.0f; + nonortho[1] = view[1]; + nonortho[2] = view[2]; + } + else if (fabsf(view[1]) < 0.1f) { + nonortho[0] = view[0]; + nonortho[1] = view[1] + 1.0f; + nonortho[2] = view[2]; + } + else { + nonortho[0] = view[0]; + nonortho[1] = view[1]; + nonortho[2] = view[2] + 1.0f; + } + + // get a vector in the plane of the view + cross_v3_v3v3(ortho, nonortho, view); + normalize_v3(ortho); + + // make a point on the surface of the brush tagent to the view + mul_v3_fl(ortho, radius); + add_v3_v3v3(offset, location, ortho); + + // project the center of the brush, and the tangent point to the view onto the screen + project_float(vc->ar, location, p1); + project_float(vc->ar, offset, p2); + + // the distance between these points is the size of the projected brush in pixels + return len_v2v2(p1, p2); +} + +static int sculpt_get_brush_geometry(bContext* C, ViewContext *vc, + int x, int y, int* pixel_radius, + float location[3]) +{ + Scene *scene = CTX_data_scene(C); + Paint *paint = paint_get_active(scene); + Brush *brush = paint_brush(paint); + float window[2]; + int hit; + + window[0] = x + vc->ar->winrct.xmin; + window[1] = y + vc->ar->winrct.ymin; + + if(vc->obact->sculpt && vc->obact->sculpt->pbvh && + sculpt_stroke_get_location(C, location, window)) { + *pixel_radius = + project_brush_radius(vc, + brush_unprojected_radius(scene, brush), + location); + + if (*pixel_radius == 0) + *pixel_radius = brush_size(scene, brush); + + mul_m4_v3(vc->obact->obmat, location); + + hit = 1; + } + else { + Sculpt* sd = CTX_data_tool_settings(C)->sculpt; + Brush* brush = paint_brush(&sd->paint); + + *pixel_radius = brush_size(scene, brush); + hit = 0; + } + + return hit; +} + +/* Draw an overlay that shows what effect the brush's texture will + have on brush strength */ +/* TODO: sculpt only for now */ +static void paint_draw_alpha_overlay(Sculpt *sd, Brush *brush, + ViewContext *vc, int x, int y) +{ + rctf quad; + + /* check for overlay mode */ + if(!(brush->flag & BRUSH_TEXTURE_OVERLAY) || + !(ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED))) + return; + + /* save lots of GL state + TODO: check on whether all of these are needed? */ + glPushAttrib(GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + if(load_tex(sd, brush, vc)) { + glEnable(GL_BLEND); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + glDepthFunc(GL_ALWAYS); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadIdentity(); + + if(brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + /* brush rotation */ + glTranslatef(0.5, 0.5, 0); + glRotatef((double)RAD2DEGF((brush->flag & BRUSH_RAKE) ? + sd->last_angle : sd->special_rotation), + 0.0, 0.0, 1.0); + glTranslatef(-0.5f, -0.5f, 0); + + /* scale based on tablet pressure */ + if(sd->draw_pressure && brush_use_size_pressure(vc->scene, brush)) { + glTranslatef(0.5f, 0.5f, 0); + glScalef(1.0f/sd->pressure_value, 1.0f/sd->pressure_value, 1); + glTranslatef(-0.5f, -0.5f, 0); + } + + if(sd->draw_anchored) { + const float *aim = sd->anchored_initial_mouse; + const rcti *win = &vc->ar->winrct; + quad.xmin = aim[0]-sd->anchored_size - win->xmin; + quad.ymin = aim[1]-sd->anchored_size - win->ymin; + quad.xmax = aim[0]+sd->anchored_size - win->xmin; + quad.ymax = aim[1]+sd->anchored_size - win->ymin; + } + else { + const int radius= brush_size(vc->scene, brush); + quad.xmin = x - radius; + quad.ymin = y - radius; + quad.xmax = x + radius; + quad.ymax = y + radius; + } + } + else { + quad.xmin = 0; + quad.ymin = 0; + quad.xmax = vc->ar->winrct.xmax - vc->ar->winrct.xmin; + quad.ymax = vc->ar->winrct.ymax - vc->ar->winrct.ymin; + } + + /* 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); + + /* draw textured quad */ + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2f(quad.xmin, quad.ymin); + glTexCoord2f(1, 0); + glVertex2f(quad.xmax, quad.ymin); + glTexCoord2f(1, 1); + glVertex2f(quad.xmax, quad.ymax); + glTexCoord2f(0, 1); + glVertex2f(quad.xmin, quad.ymax); + glEnd(); + + glPopMatrix(); + } + + glPopAttrib(); +} + +/* Special actions taken when paint cursor goes over mesh */ +/* TODO: sculpt only for now */ +static void paint_cursor_on_hit(Sculpt *sd, Brush *brush, ViewContext *vc, + const float location[3]) +{ + float unprojected_radius, projected_radius; + + /* update the brush's cached 3D radius */ + if(!brush_use_locked_size(vc->scene, brush)) { + /* get 2D brush radius */ + if(sd->draw_anchored) + projected_radius = sd->anchored_size; + else { + if(brush->flag & BRUSH_ANCHORED) + projected_radius = 8; + else + projected_radius = brush_size(vc->scene, brush); + } + + /* convert brush radius from 2D to 3D */ + unprojected_radius = paint_calc_object_space_radius(vc, location, + projected_radius); + + /* scale 3D brush radius by pressure */ + if(sd->draw_pressure && brush_use_size_pressure(vc->scene, brush)) + unprojected_radius *= sd->pressure_value; + + /* set cached value in either Brush or UnifiedPaintSettings */ + brush_set_unprojected_radius(vc->scene, brush, unprojected_radius); + } +} + +static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) +{ + Scene *scene = CTX_data_scene(C); + Paint *paint = paint_get_active(scene); + Brush *brush = paint_brush(paint); + ViewContext vc; + float final_radius; + float translation[2]; + float outline_alpha, *outline_col; + + /* set various defaults */ + translation[0] = x; + translation[1] = y; + outline_alpha = 0.5; + outline_col = brush->add_col; + final_radius = brush_size(scene, brush); + + /* check that brush drawing is enabled */ + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; + + /* can't use stroke vc here because this will be called during + mouse over too, not just during a stroke */ + view3d_set_viewcontext(C, &vc); + + /* TODO: as sculpt and other paint modes are unified, this + special mode of drawing will go away */ + if(vc.obact->sculpt) { + Sculpt *sd = CTX_data_tool_settings(C)->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 = sd->last_x - x; + const float dy = sd->last_y - y; + + if(dx*dx + dy*dy >= r*r) { + sd->last_angle = atan2(dx, dy); + + sd->last_x = u*sd->last_x + v*x; + sd->last_y = u*sd->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(sd, brush, &vc, x, y); + + if(brush_use_locked_size(scene, brush)) + brush_set_size(scene, brush, pixel_radius); + + /* check if brush is subtracting, use different color then */ + /* TODO: no way currently to know state of pen flip or + invert key modifier without starting a stroke */ + if((!(brush->flag & BRUSH_INVERTED) ^ + !(brush->flag & BRUSH_DIR_IN)) && + ELEM5(brush->sculpt_tool, SCULPT_TOOL_DRAW, + SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, + SCULPT_TOOL_PINCH, SCULPT_TOOL_CREASE)) + outline_col = brush->sub_col; + + /* only do if brush is over the mesh */ + if(hit) + paint_cursor_on_hit(sd, brush, &vc, location); + + if(sd->draw_anchored) { + final_radius = sd->anchored_size; + translation[0] = sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin; + translation[1] = sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin; + } + } + + /* make lines pretty */ + glEnable(GL_BLEND); + glEnable(GL_LINE_SMOOTH); + + /* set brush color */ + glColor4f(outline_col[0], outline_col[1], outline_col[2], outline_alpha); + + /* draw brush outline */ + glTranslatef(translation[0], translation[1], 0); + glutil_draw_lined_arc(0.0, M_PI*2.0, final_radius, 40); + glTranslatef(-translation[0], -translation[1], 0); + + /* restore GL state */ + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); +} + +/* Public API */ + +void paint_cursor_start(bContext *C, int (*poll)(bContext *C)) +{ + Paint *p = paint_get_active(CTX_data_scene(C)); + + if(p && !p->paint_cursor) + p->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, paint_draw_cursor, NULL); +} diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 4ca05e1fbd7..73dc1e236f6 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -50,7 +50,7 @@ struct wmOperator; struct wmOperatorType; /* paint_stroke.c */ -typedef int (*StrokeGetLocation)(struct bContext *C, struct PaintStroke *stroke, float location[3], float mouse[2]); +typedef int (*StrokeGetLocation)(struct bContext *C, float location[3], float mouse[2]); typedef int (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, struct wmEvent *event); typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr); typedef void (*StrokeDone)(struct bContext *C, struct PaintStroke *stroke); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 67bd6ca9255..cd8262f06bd 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -56,9 +56,6 @@ #include "ED_view3d.h" #include "paint_intern.h" -/* still needed for sculpt_stroke_get_location, should be - removed eventually (TODO) */ -#include "sculpt_intern.h" #include <float.h> #include <math.h> @@ -108,552 +105,6 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata glDisable(GL_LINE_SMOOTH); } -typedef struct Snapshot { - float size[3]; - float ofs[3]; - float rot; - int brush_size; - int winx; - int winy; - int brush_map_mode; - int curve_changed_timestamp; -} Snapshot; - -static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc) -{ - MTex* mtex = &brush->mtex; - - return (((mtex->tex) && - equals_v3v3(mtex->ofs, snap->ofs) && - equals_v3v3(mtex->size, snap->size) && - mtex->rot == snap->rot) && - - /* make brush smaller shouldn't cause a resample */ - ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && - (brush_size(vc->scene, brush) <= snap->brush_size)) || - (brush_size(vc->scene, brush) == snap->brush_size)) && - - (mtex->brush_map_mode == snap->brush_map_mode) && - (vc->ar->winx == snap->winx) && - (vc->ar->winy == snap->winy)); -} - -static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc) -{ - if (brush->mtex.tex) { - snap->brush_map_mode = brush->mtex.brush_map_mode; - copy_v3_v3(snap->ofs, brush->mtex.ofs); - copy_v3_v3(snap->size, brush->mtex.size); - snap->rot = brush->mtex.rot; - } - else { - snap->brush_map_mode = -1; - snap->ofs[0]= snap->ofs[1]= snap->ofs[2]= -1; - snap->size[0]= snap->size[1]= snap->size[2]= -1; - snap->rot = -1; - } - - snap->brush_size = brush_size(vc->scene, brush); - snap->winx = vc->ar->winx; - snap->winy = vc->ar->winy; -} - -static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) -{ - static GLuint overlay_texture = 0; - static int init = 0; - static int tex_changed_timestamp = -1; - static int curve_changed_timestamp = -1; - static Snapshot snap; - static int old_size = -1; - - GLubyte* buffer = NULL; - - int size; - int j; - int refresh; - -#ifndef _OPENMP - (void)sd; /* quied unused warning */ -#endif - - if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0; - - refresh = - !overlay_texture || - (br->mtex.tex && - (!br->mtex.tex->preview || - br->mtex.tex->preview->changed_timestamp[0] != tex_changed_timestamp)) || - !br->curve || - br->curve->changed_timestamp != curve_changed_timestamp || - !same_snap(&snap, br, vc); - - if (refresh) { - if (br->mtex.tex && br->mtex.tex->preview) - tex_changed_timestamp = br->mtex.tex->preview->changed_timestamp[0]; - - if (br->curve) - curve_changed_timestamp = br->curve->changed_timestamp; - - make_snap(&snap, br, vc); - - if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { - int s = brush_size(vc->scene, br); - int r = 1; - - for (s >>= 1; s > 0; s >>= 1) - r++; - - size = (1<<r); - - if (size < 256) - size = 256; - - if (size < old_size) - size = old_size; - } - else - size = 512; - - if (old_size != size) { - if (overlay_texture) { - glDeleteTextures(1, &overlay_texture); - overlay_texture = 0; - } - - init = 0; - - old_size = size; - } - - buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "load_tex"); - - #pragma omp parallel for schedule(static) if (sd->flags & SCULPT_USE_OPENMP) - for (j= 0; j < size; j++) { - int i; - float y; - float len; - - for (i= 0; i < size; i++) { - - // largely duplicated from tex_strength - - const float rotation = -br->mtex.rot; - float radius = brush_size(vc->scene, br); - int index = j*size + i; - float x; - float avg; - - 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 *= 2; - y *= 2; - } - - len = sqrtf(x*x + y*y); - - if ((br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) || 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. */ - if (br->mtex.tex && (rotation > 0.001f || rotation < -0.001f)) { - const float angle = atan2f(y, x) + rotation; - - x = len * cosf(angle); - y = len * sinf(angle); - } - - x *= br->mtex.size[0]; - y *= br->mtex.size[1]; - - x += br->mtex.ofs[0]; - y += br->mtex.ofs[1]; - - avg = br->mtex.tex ? paint_get_tex_pixel(br, x, y) : 1; - - avg += br->texture_sample_bias; - - if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) - avg *= brush_curve_strength(br, len, 1); /* Falloff curve */ - - buffer[index] = 255 - (GLubyte)(255*avg); - } - else { - buffer[index] = 0; - } - } - } - - if (!overlay_texture) - glGenTextures(1, &overlay_texture); - } - else { - size= old_size; - } - - 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); - init = 1; - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); - } - - if (buffer) - MEM_freeN(buffer); - } - - glEnable(GL_TEXTURE_2D); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - } - - return 1; -} - -static int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats) -{ - float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; - - ED_view3d_global_to_vector(rv3d, location, view); - - // create a vector that is not orthogonal to view - - if (fabsf(view[0]) < 0.1f) { - nonortho[0] = view[0] + 1.0f; - nonortho[1] = view[1]; - nonortho[2] = view[2]; - } - else if (fabsf(view[1]) < 0.1f) { - nonortho[0] = view[0]; - nonortho[1] = view[1] + 1.0f; - nonortho[2] = view[2]; - } - else { - nonortho[0] = view[0]; - nonortho[1] = view[1]; - nonortho[2] = view[2] + 1.0f; - } - - // get a vector in the plane of the view - cross_v3_v3v3(ortho, nonortho, view); - normalize_v3(ortho); - - // make a point on the surface of the brush tagent to the view - mul_v3_fl(ortho, radius); - add_v3_v3v3(offset, location, ortho); - - // project the center of the brush, and the tagent point to the view onto the screen - projectf(mats, location, p1); - projectf(mats, offset, p2); - - // the distance between these points is the size of the projected brush in pixels - return len_v2v2(p1, p2); -} - -static int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, - float location[3]) -{ - struct PaintStroke *stroke; - const Scene *scene = CTX_data_scene(C); - float window[2]; - int hit; - - stroke = paint_stroke_new(C, NULL, NULL, NULL, NULL, 0); - - window[0] = x + stroke->vc.ar->winrct.xmin; - window[1] = y + stroke->vc.ar->winrct.ymin; - - if(stroke->vc.obact->sculpt && stroke->vc.obact->sculpt->pbvh && - sculpt_stroke_get_location(C, stroke, location, window)) { - *pixel_radius = project_brush_radius(stroke->vc.rv3d, - brush_unprojected_radius(scene, stroke->brush), - location, &stroke->mats); - - if (*pixel_radius == 0) - *pixel_radius = brush_size(scene, stroke->brush); - - mul_m4_v3(stroke->vc.obact->obmat, location); - - hit = 1; - } - else { - Sculpt* sd = CTX_data_tool_settings(C)->sculpt; - Brush* brush = paint_brush(&sd->paint); - - *pixel_radius = brush_size(scene, brush); - hit = 0; - } - - paint_stroke_free(stroke); - - return hit; -} - -/* Draw an overlay that shows what effect the brush's texture will - have on brush strength */ -/* TODO: sculpt only for now */ -static void paint_draw_alpha_overlay(Sculpt *sd, Brush *brush, - ViewContext *vc, int x, int y) -{ - rctf quad; - - /* check for overlay mode */ - if(!(brush->flag & BRUSH_TEXTURE_OVERLAY) || - !(ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED))) - return; - - /* save lots of GL state - TODO: check on whether all of these are needed? */ - glPushAttrib(GL_COLOR_BUFFER_BIT| - GL_CURRENT_BIT| - GL_DEPTH_BUFFER_BIT| - GL_ENABLE_BIT| - GL_LINE_BIT| - GL_POLYGON_BIT| - GL_STENCIL_BUFFER_BIT| - GL_TRANSFORM_BIT| - GL_VIEWPORT_BIT| - GL_TEXTURE_BIT); - - if(load_tex(sd, brush, vc)) { - glEnable(GL_BLEND); - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDepthMask(GL_FALSE); - glDepthFunc(GL_ALWAYS); - - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glLoadIdentity(); - - if(brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { - /* brush rotation */ - glTranslatef(0.5, 0.5, 0); - glRotatef((double)RAD2DEGF((brush->flag & BRUSH_RAKE) ? - sd->last_angle : sd->special_rotation), - 0.0, 0.0, 1.0); - glTranslatef(-0.5f, -0.5f, 0); - - /* scale based on tablet pressure */ - if(sd->draw_pressure && brush_use_size_pressure(vc->scene, brush)) { - glTranslatef(0.5f, 0.5f, 0); - glScalef(1.0f/sd->pressure_value, 1.0f/sd->pressure_value, 1); - glTranslatef(-0.5f, -0.5f, 0); - } - - if(sd->draw_anchored) { - const float *aim = sd->anchored_initial_mouse; - const rcti *win = &vc->ar->winrct; - quad.xmin = aim[0]-sd->anchored_size - win->xmin; - quad.ymin = aim[1]-sd->anchored_size - win->ymin; - quad.xmax = aim[0]+sd->anchored_size - win->xmin; - quad.ymax = aim[1]+sd->anchored_size - win->ymin; - } - else { - const int radius= brush_size(vc->scene, brush); - quad.xmin = x - radius; - quad.ymin = y - radius; - quad.xmax = x + radius; - quad.ymax = y + radius; - } - } - else { - quad.xmin = 0; - quad.ymin = 0; - quad.xmax = vc->ar->winrct.xmax - vc->ar->winrct.xmin; - quad.ymax = vc->ar->winrct.ymax - vc->ar->winrct.ymin; - } - - /* 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); - - /* draw textured quad */ - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2f(quad.xmin, quad.ymin); - glTexCoord2f(1, 0); - glVertex2f(quad.xmax, quad.ymin); - glTexCoord2f(1, 1); - glVertex2f(quad.xmax, quad.ymax); - glTexCoord2f(0, 1); - glVertex2f(quad.xmin, quad.ymax); - glEnd(); - - glPopMatrix(); - } - - glPopAttrib(); -} - -/* Special actions taken when paint cursor goes over mesh */ -/* TODO: sculpt only for now */ -static void paint_cursor_on_hit(Sculpt *sd, Brush *brush, ViewContext *vc, - const float location[3]) -{ - float unprojected_radius, projected_radius; - - /* update the brush's cached 3D radius */ - if(!brush_use_locked_size(vc->scene, brush)) { - /* get 2D brush radius */ - if(sd->draw_anchored) - projected_radius = sd->anchored_size; - else { - if(brush->flag & BRUSH_ANCHORED) - projected_radius = 8; - else - projected_radius = brush_size(vc->scene, brush); - } - - /* convert brush radius from 2D to 3D */ - unprojected_radius = paint_calc_object_space_radius(vc, location, - projected_radius); - - /* scale 3D brush radius by pressure */ - if(sd->draw_pressure && brush_use_size_pressure(vc->scene, brush)) - unprojected_radius *= sd->pressure_value; - - /* set cached value in either Brush or UnifiedPaintSettings */ - brush_set_unprojected_radius(vc->scene, brush, unprojected_radius); - } -} - -static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) -{ - Scene *scene = CTX_data_scene(C); - Paint *paint = paint_get_active(scene); - Brush *brush = paint_brush(paint); - ViewContext vc; - float final_radius; - float translation[2]; - float outline_alpha, *outline_col; - - /* set various defaults */ - translation[0] = x; - translation[1] = y; - outline_alpha = 0.5; - outline_col = brush->add_col; - final_radius = brush_size(scene, brush); - - /* check that brush drawing is enabled */ - if(!(paint->flags & PAINT_SHOW_BRUSH)) - return; - - /* can't use stroke vc here because this will be called during - mouse over too, not just during a stroke */ - view3d_set_viewcontext(C, &vc); - - /* TODO: as sculpt and other paint modes are unified, this - special mode of drawing will go away */ - if(vc.obact->sculpt) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - float location[3]; - int pixel_radius, hit; - const float root_alpha = brush_alpha(scene, brush); - float visual_strength = root_alpha*root_alpha; - const float min_alpha = 0.20f; - const float max_alpha = 0.80f; - - /* 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 = sd->last_x - x; - const float dy = sd->last_y - y; - - if(dx*dx + dy*dy >= r*r) { - sd->last_angle = atan2(dx, dy); - - sd->last_x = u*sd->last_x + v*x; - sd->last_y = u*sd->last_y + v*y; - } - } - - /* test if brush is over the mesh */ - hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location); - - /* draw overlay */ - paint_draw_alpha_overlay(sd, brush, &vc, x, y); - - if(brush_use_locked_size(scene, brush)) - brush_set_size(scene, brush, pixel_radius); - - /* check if brush is subtracting, use different color then */ - /* TODO: no way currently to know state of pen flip or - invert key modifier without starting a stroke */ - if((!(brush->flag & BRUSH_INVERTED) ^ - !(brush->flag & BRUSH_DIR_IN)) && - ELEM5(brush->sculpt_tool, SCULPT_TOOL_DRAW, - SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, - SCULPT_TOOL_PINCH, SCULPT_TOOL_CREASE)) - outline_col = brush->sub_col; - - /* only do if brush is over the mesh */ - if(hit) { - /* scale the alpha by pen pressure */ - if(sd->draw_pressure && brush_use_alpha_pressure(vc.scene, brush)) - visual_strength *= sd->pressure_value; - - paint_cursor_on_hit(sd, brush, &vc, location); - } - - /* don't show effect of strength past the soft limit */ - if(visual_strength > 1) - visual_strength = 1; - - outline_alpha = ((paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) ? - min_alpha + (visual_strength*(max_alpha-min_alpha)) : 0.50f); - - if(sd->draw_anchored) { - final_radius = sd->anchored_size; - translation[0] = sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin; - translation[1] = sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin; - } - } - - /* make lines pretty */ - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); - - /* set brush color */ - glColor4f(outline_col[0], outline_col[1], outline_col[2], outline_alpha); - - /* draw brush outline */ - glTranslatef(translation[0], translation[1], 0); - glutil_draw_lined_arc(0.0, M_PI*2.0, final_radius, 40); - glTranslatef(-translation[0], -translation[1], 0); - - /* restore GL state */ - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); -} - /* if this is a tablet event, return tablet pressure and set *pen_flip to 1 if the eraser tool is being used, 0 otherwise */ static float event_tablet_data(wmEvent *event, int *pen_flip) @@ -712,7 +163,7 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev /* TODO: can remove the if statement once all modes have this */ if(stroke->get_location) - stroke->get_location(C, stroke, location, mouse); + stroke->get_location(C, location, mouse); else zero_v3(location); @@ -976,12 +427,3 @@ int paint_poll(bContext *C) CTX_wm_area(C)->spacetype == SPACE_VIEW3D && CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW; } - -void paint_cursor_start(bContext *C, int (*poll)(bContext *C)) -{ - Paint *p = paint_get_active(CTX_data_scene(C)); - - if(p && !p->paint_cursor) - p->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, paint_draw_cursor, NULL); -} - diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index ae5316e71c4..308534ab35c 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3105,7 +3105,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, halfway[0] = (float)dx * 0.5f + cache->initial_mouse[0]; halfway[1] = (float)dy * 0.5f + cache->initial_mouse[1]; - if (sculpt_stroke_get_location(C, stroke, out, halfway)) { + if (sculpt_stroke_get_location(C, out, halfway)) { copy_v3_v3(sd->anchored_location, out); copy_v2_v2(sd->anchored_initial_mouse, halfway); copy_v2_v2(cache->tex_mouse, halfway); @@ -3207,23 +3207,29 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin) (This allows us to ignore the GL depth buffer) Returns 0 if the ray doesn't hit the mesh, non-zero otherwise */ -int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]) +int sculpt_stroke_get_location(bContext *C, float out[3], float mouse[2]) { - ViewContext *vc = paint_stroke_view_context(stroke); - Object *ob = vc->obact; - SculptSession *ss= ob->sculpt; - StrokeCache *cache= ss->cache; + ViewContext vc; + Object *ob; + SculptSession *ss; + StrokeCache *cache; float ray_start[3], ray_end[3], ray_normal[3], dist; float obimat[4][4]; float mval[2]; SculptRaycastData srd; - mval[0] = mouse[0] - vc->ar->winrct.xmin; - mval[1] = mouse[1] - vc->ar->winrct.ymin; + view3d_set_viewcontext(C, &vc); + + ob = vc.obact; + ss = ob->sculpt; + cache = ss->cache; sculpt_stroke_modifiers_check(C, ob); - ED_view3d_win_to_segment_clip(vc->ar, vc->v3d, mval, ray_start, ray_end); + mval[0] = mouse[0] - vc.ar->winrct.xmin; + mval[1] = mouse[1] - vc.ar->winrct.ymin; + + ED_view3d_win_to_segment_clip(vc.ar, vc.v3d, mval, ray_start, ray_end); invert_m4_m4(obimat, ob->obmat); mul_m4_v3(obimat, ray_start); @@ -3232,7 +3238,7 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou sub_v3_v3v3(ray_normal, ray_end, ray_start); dist= normalize_v3(ray_normal); - srd.ss = vc->obact->sculpt; + srd.ss = vc.obact->sculpt; srd.ray_start = ray_start; srd.ray_normal = ray_normal; srd.dist = dist; @@ -3371,14 +3377,14 @@ static void sculpt_flush_update(bContext *C) /* Returns whether the mouse/stylus is over the mesh (1) or over the background (0) */ -static int over_mesh(bContext *C, struct wmOperator *op, float x, float y) +static int over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float y) { float mouse[2], co[3]; mouse[0] = x; mouse[1] = y; - return sculpt_stroke_get_location(C, op->customdata, co, mouse); + return sculpt_stroke_get_location(C, co, mouse); } static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 42205a8da04..275d1d52355 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -73,7 +73,7 @@ void sculpt_stroke_free(struct SculptStroke *); void sculpt_stroke_add_point(struct SculptStroke *, const short x, const short y); void sculpt_stroke_apply(struct Sculpt *sd, struct SculptStroke *); void sculpt_stroke_apply_all(struct Sculpt *sd, struct SculptStroke *); -int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]); +int sculpt_stroke_get_location(bContext *C, float out[3], float mouse[2]); /* Undo */ |