2009-08-19 21:24:52 +00:00
|
|
|
/*
|
|
|
|
* ***** 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,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-08-19 21:24:52 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2009 by Nicholas Bishop
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* The Original Code is: all of this file.
|
|
|
|
*
|
2010-07-14 14:11:03 +00:00
|
|
|
* Contributor(s): Jason Wilkins, Tom Musgrove.
|
2009-08-19 21:24:52 +00:00
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-02-27 20:29:51 +00:00
|
|
|
/** \file blender/editors/sculpt_paint/paint_stroke.c
|
|
|
|
* \ingroup edsculpt
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2011-01-07 18:36:47 +00:00
|
|
|
#include "BLI_math.h"
|
|
|
|
#include "BLI_utildefines.h"
|
2013-03-13 03:46:22 +00:00
|
|
|
#include "BLI_rand.h"
|
2011-01-07 18:36:47 +00:00
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
2010-08-04 04:01:27 +00:00
|
|
|
#include "DNA_brush_types.h"
|
2009-08-19 21:24:52 +00:00
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "BKE_paint.h"
|
2010-07-14 14:11:03 +00:00
|
|
|
#include "BKE_brush.h"
|
2009-08-19 21:24:52 +00:00
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
2009-08-20 05:13:07 +00:00
|
|
|
#include "BIF_gl.h"
|
2009-08-19 21:24:52 +00:00
|
|
|
#include "BIF_glutil.h"
|
|
|
|
|
|
|
|
#include "ED_screen.h"
|
|
|
|
#include "ED_view3d.h"
|
|
|
|
|
|
|
|
#include "paint_intern.h"
|
|
|
|
|
|
|
|
#include <float.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
2012-05-21 23:32:46 +00:00
|
|
|
typedef struct PaintSample {
|
|
|
|
float mouse[2];
|
2013-05-18 11:25:24 +00:00
|
|
|
float pressure;
|
2012-05-21 23:32:46 +00:00
|
|
|
} PaintSample;
|
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
typedef struct PaintStroke {
|
2009-08-20 05:13:07 +00:00
|
|
|
void *mode_data;
|
|
|
|
void *smooth_stroke_cursor;
|
2009-08-21 00:46:36 +00:00
|
|
|
wmTimer *timer;
|
2009-08-20 05:13:07 +00:00
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
/* Cached values */
|
|
|
|
ViewContext vc;
|
|
|
|
bglMats mats;
|
|
|
|
Brush *brush;
|
|
|
|
|
2012-05-21 23:32:46 +00:00
|
|
|
/* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
|
|
|
|
* to smooth the stroke */
|
|
|
|
PaintSample samples[PAINT_MAX_INPUT_SAMPLES];
|
|
|
|
int num_samples;
|
|
|
|
int cur_sample;
|
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
float last_mouse_position[2];
|
|
|
|
|
2010-07-20 10:41:08 +00:00
|
|
|
/* Set whether any stroke step has yet occurred
|
2012-03-03 16:31:46 +00:00
|
|
|
* e.g. in sculpt mode, stroke doesn't start until cursor
|
|
|
|
* passes over the mesh */
|
2009-08-19 21:24:52 +00:00
|
|
|
int stroke_started;
|
2011-03-01 16:26:37 +00:00
|
|
|
/* event that started stroke, for modal() return */
|
|
|
|
int event_type;
|
2013-05-18 15:24:25 +00:00
|
|
|
/* check if stroke variables have been initialized */
|
|
|
|
bool stroke_init;
|
|
|
|
/* check if various brush mapping variables have been initialized */
|
2013-03-13 03:46:22 +00:00
|
|
|
bool brush_init;
|
|
|
|
float initial_mouse[2];
|
2013-05-18 09:44:30 +00:00
|
|
|
/* cached_pressure stores initial pressure for size pressure influence mainly */
|
2013-03-13 03:46:22 +00:00
|
|
|
float cached_pressure;
|
2013-05-18 09:44:30 +00:00
|
|
|
/* last pressure will store last pressure value for use in interpolation for space strokes */
|
|
|
|
float last_pressure;
|
|
|
|
|
2013-03-13 03:46:22 +00:00
|
|
|
|
2013-03-15 09:19:41 +00:00
|
|
|
float zoom_2d;
|
2013-05-18 09:44:30 +00:00
|
|
|
int pen_flip;
|
2013-03-15 09:19:41 +00:00
|
|
|
|
2009-10-27 19:53:34 +00:00
|
|
|
StrokeGetLocation get_location;
|
2009-08-19 21:24:52 +00:00
|
|
|
StrokeTestStart test_start;
|
|
|
|
StrokeUpdateStep update_step;
|
2013-05-15 14:37:05 +00:00
|
|
|
StrokeRedraw redraw;
|
2009-08-19 21:24:52 +00:00
|
|
|
StrokeDone done;
|
|
|
|
} PaintStroke;
|
|
|
|
|
2009-08-20 05:13:07 +00:00
|
|
|
/*** Cursor ***/
|
|
|
|
static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata)
|
|
|
|
{
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
Paint *paint = BKE_paint_get_active_from_context(C);
|
|
|
|
Brush *brush = BKE_paint_brush(paint);
|
2009-08-20 05:13:07 +00:00
|
|
|
PaintStroke *stroke = customdata;
|
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (stroke && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) {
|
2013-03-10 11:37:27 +00:00
|
|
|
glColor4ubv(paint->paint_cursor_col);
|
|
|
|
glEnable(GL_LINE_SMOOTH);
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
|
|
|
|
sdrawline(x, y, (int)stroke->last_mouse_position[0],
|
|
|
|
(int)stroke->last_mouse_position[1]);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDisable(GL_LINE_SMOOTH);
|
2009-08-20 05:13:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-08 01:45:02 +00:00
|
|
|
/* if this is a tablet event, return tablet pressure and set *pen_flip
|
2012-03-03 16:31:46 +00:00
|
|
|
* to 1 if the eraser tool is being used, 0 otherwise */
|
2013-03-13 09:03:46 +00:00
|
|
|
static float event_tablet_data(const wmEvent *event, int *pen_flip)
|
2009-08-19 21:24:52 +00:00
|
|
|
{
|
2011-01-08 01:45:02 +00:00
|
|
|
int erasor = 0;
|
|
|
|
float pressure = 1;
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2013-01-17 03:41:23 +00:00
|
|
|
if (event->tablet_data) {
|
|
|
|
wmTabletData *wmtab = event->tablet_data;
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2011-01-08 01:45:02 +00:00
|
|
|
erasor = (wmtab->Active == EVT_TABLET_ERASER);
|
|
|
|
pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1;
|
|
|
|
}
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (pen_flip)
|
2011-01-08 01:45:02 +00:00
|
|
|
(*pen_flip) = erasor;
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2011-01-08 01:45:02 +00:00
|
|
|
return pressure;
|
|
|
|
}
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2013-03-15 22:32:44 +00:00
|
|
|
|
2013-03-13 03:46:22 +00:00
|
|
|
/* Initialize the stroke cache variants from operator properties */
|
|
|
|
static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode,
|
|
|
|
struct PaintStroke *stroke,
|
2013-03-16 14:42:05 +00:00
|
|
|
const float mouse[2], float pressure)
|
2013-03-13 03:46:22 +00:00
|
|
|
{
|
|
|
|
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);
|
2013-04-22 10:46:01 +00:00
|
|
|
copy_v2_v2(ups->mask_tex_mouse, mouse);
|
2013-03-13 03:46:22 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-03-30 09:57:35 +00:00
|
|
|
if (paint_supports_dynamic_tex_coords(brush, mode)) {
|
2013-03-25 01:00:16 +00:00
|
|
|
if (((brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) ||
|
2013-03-25 02:41:30 +00:00
|
|
|
(brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM)) &&
|
2013-03-13 03:46:22 +00:00
|
|
|
!(brush->flag & BRUSH_RAKE))
|
|
|
|
{
|
2013-03-25 01:42:58 +00:00
|
|
|
if (brush->flag & BRUSH_RANDOM_ROTATION)
|
|
|
|
ups->brush_rotation = 2.0f * (float)M_PI * BLI_frand();
|
|
|
|
else
|
|
|
|
ups->brush_rotation = 0.0f;
|
2013-03-13 03:46:22 +00:00
|
|
|
}
|
2013-03-25 01:00:16 +00:00
|
|
|
|
2013-04-01 07:57:33 +00:00
|
|
|
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM)
|
2013-04-22 10:46:01 +00:00
|
|
|
BKE_brush_randomize_texture_coordinates(ups, false);
|
|
|
|
else {
|
2013-03-25 01:00:16 +00:00
|
|
|
copy_v2_v2(ups->tex_mouse, mouse);
|
2013-04-22 10:46:01 +00:00
|
|
|
}
|
2013-03-13 03:46:22 +00:00
|
|
|
}
|
|
|
|
|
2013-04-22 10:46:01 +00:00
|
|
|
/* take care of mask texture, if any */
|
|
|
|
if (brush->mask_mtex.tex) {
|
|
|
|
if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM)
|
|
|
|
BKE_brush_randomize_texture_coordinates(ups, true);
|
|
|
|
else {
|
|
|
|
copy_v2_v2(ups->mask_tex_mouse, mouse);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-13 03:46:22 +00:00
|
|
|
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) {
|
2013-03-13 05:23:53 +00:00
|
|
|
if (stroke->get_location(C, out, halfway)) {
|
2013-03-13 03:46:22 +00:00
|
|
|
hit = true;
|
|
|
|
}
|
2013-03-13 05:23:53 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-13 03:46:22 +00:00
|
|
|
hit = true;
|
|
|
|
}
|
|
|
|
}
|
2013-03-13 05:23:53 +00:00
|
|
|
if (hit) {
|
2013-03-13 03:46:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2013-03-15 22:32:44 +00:00
|
|
|
|
2013-03-13 03:46:22 +00:00
|
|
|
|
2011-01-08 01:45:02 +00:00
|
|
|
/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
|
2013-05-18 09:44:30 +00:00
|
|
|
static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float mouse_in[2], float pressure)
|
2011-01-08 01:45:02 +00:00
|
|
|
{
|
2012-01-14 23:54:51 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2013-05-13 13:32:42 +00:00
|
|
|
wmWindow *window = CTX_wm_window(C);
|
2013-05-01 14:59:50 +00:00
|
|
|
ARegion *ar = CTX_wm_region(C);
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
Paint *paint = BKE_paint_get_active_from_context(C);
|
|
|
|
PaintMode mode = BKE_paintmode_get_active_from_context(C);
|
|
|
|
Brush *brush = BKE_paint_brush(paint);
|
2009-08-19 21:24:52 +00:00
|
|
|
PaintStroke *stroke = op->customdata;
|
2012-10-08 07:08:29 +00:00
|
|
|
float mouse_out[2];
|
2011-01-08 01:45:02 +00:00
|
|
|
PointerRNA itemptr;
|
|
|
|
float location[3];
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2013-03-10 17:40:55 +00:00
|
|
|
/* the following code is adapted from texture paint. It may not be needed but leaving here
|
|
|
|
* just in case for reference (code in texpaint removed as part of refactoring).
|
|
|
|
* It's strange that only texpaint had these guards. */
|
|
|
|
#if 0
|
|
|
|
/* special exception here for too high pressure values on first touch in
|
|
|
|
* windows for some tablets, then we just skip first touch .. */
|
|
|
|
if (tablet && (pressure >= 0.99f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || BKE_brush_use_alpha_pressure(scene, pop->s.brush) || BKE_brush_use_size_pressure(scene, pop->s.brush)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* This can be removed once fixed properly in
|
|
|
|
* BKE_brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, float pressure, void *user)
|
|
|
|
* at zero pressure we should do nothing 1/2^12 is 0.0002 which is the sensitivity of the most sensitive pen tablet available */
|
|
|
|
if (tablet && (pressure < 0.0002f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || BKE_brush_use_alpha_pressure(scene, pop->s.brush) || BKE_brush_use_size_pressure(scene, pop->s.brush)))
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2013-03-10 20:05:18 +00:00
|
|
|
/* copy last position -before- jittering, or space fill code
|
|
|
|
* will create too many dabs */
|
|
|
|
copy_v2_v2(stroke->last_mouse_position, mouse_in);
|
2013-05-18 11:25:24 +00:00
|
|
|
stroke->last_pressure = pressure;
|
2013-03-10 20:05:18 +00:00
|
|
|
|
2013-03-15 22:32:44 +00:00
|
|
|
paint_brush_update(C, brush, mode, stroke, mouse_in, pressure);
|
|
|
|
|
2013-03-30 11:40:09 +00:00
|
|
|
{
|
2011-09-16 06:47:01 +00:00
|
|
|
float delta[2];
|
2013-03-15 09:19:41 +00:00
|
|
|
float factor = stroke->zoom_2d;
|
|
|
|
|
|
|
|
if (brush->flag & BRUSH_JITTER_PRESSURE)
|
|
|
|
factor *= pressure;
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2012-10-08 07:08:29 +00:00
|
|
|
BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out);
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2011-01-08 01:45:02 +00:00
|
|
|
/* XXX: meh, this is round about because
|
2012-05-05 00:58:22 +00:00
|
|
|
* BKE_brush_jitter_pos isn't written in the best way to
|
2012-03-03 16:31:46 +00:00
|
|
|
* be reused here */
|
2013-03-16 14:42:05 +00:00
|
|
|
if (factor != 1.0f) {
|
2012-10-08 07:08:29 +00:00
|
|
|
sub_v2_v2v2(delta, mouse_out, mouse_in);
|
2013-03-15 09:19:41 +00:00
|
|
|
mul_v2_fl(delta, factor);
|
2012-10-08 07:08:29 +00:00
|
|
|
add_v2_v2v2(mouse_out, mouse_in, delta);
|
2010-07-14 14:11:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-08 01:45:02 +00:00
|
|
|
/* TODO: can remove the if statement once all modes have this */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (stroke->get_location)
|
2012-10-08 07:08:29 +00:00
|
|
|
stroke->get_location(C, location, mouse_out);
|
2010-07-14 14:11:03 +00:00
|
|
|
else
|
|
|
|
zero_v3(location);
|
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
/* Add to stroke */
|
|
|
|
RNA_collection_add(op->ptr, "stroke", &itemptr);
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2011-01-08 01:45:02 +00:00
|
|
|
RNA_float_set_array(&itemptr, "location", location);
|
2012-10-08 07:08:29 +00:00
|
|
|
RNA_float_set_array(&itemptr, "mouse", mouse_out);
|
2013-05-18 09:44:30 +00:00
|
|
|
RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip);
|
2011-01-08 01:45:02 +00:00
|
|
|
RNA_float_set(&itemptr, "pressure", pressure);
|
2009-08-19 21:24:52 +00:00
|
|
|
|
|
|
|
stroke->update_step(C, stroke, &itemptr);
|
2013-05-01 14:59:50 +00:00
|
|
|
|
2013-05-15 22:55:30 +00:00
|
|
|
/* don't record this for now, it takes up a lot of memory when doing long
|
|
|
|
* strokes with small brush size, and operators have register disabled */
|
|
|
|
RNA_collection_clear(op->ptr, "stroke");
|
|
|
|
|
2013-05-01 14:59:50 +00:00
|
|
|
/* always redraw region if brush is shown */
|
|
|
|
if (ar && (paint->flags & PAINT_SHOW_BRUSH))
|
2013-05-13 13:32:42 +00:00
|
|
|
WM_paint_cursor_tag_redraw(window, ar);
|
2009-08-19 21:24:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns zero if no sculpt changes should be made, non-zero otherwise */
|
2013-05-18 11:25:24 +00:00
|
|
|
static int paint_smooth_stroke(PaintStroke *stroke, float output[2], float *outpressure,
|
2013-03-14 02:27:36 +00:00
|
|
|
const PaintSample *sample, PaintMode mode)
|
2009-08-19 21:24:52 +00:00
|
|
|
{
|
2012-05-21 23:32:46 +00:00
|
|
|
output[0] = sample->mouse[0];
|
|
|
|
output[1] = sample->mouse[1];
|
2013-05-18 11:25:24 +00:00
|
|
|
*outpressure = sample->pressure;
|
2009-08-19 21:24:52 +00:00
|
|
|
|
2013-03-14 05:52:30 +00:00
|
|
|
if (paint_supports_smooth_stroke(stroke->brush, mode)) {
|
2013-03-16 14:42:05 +00:00
|
|
|
float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d;
|
2011-03-27 14:59:55 +00:00
|
|
|
float u = stroke->brush->smooth_stroke_factor, v = 1.0f - u;
|
2012-05-21 23:32:46 +00:00
|
|
|
float dx = stroke->last_mouse_position[0] - sample->mouse[0];
|
|
|
|
float dy = stroke->last_mouse_position[1] - sample->mouse[1];
|
2009-08-19 21:24:52 +00:00
|
|
|
|
|
|
|
/* If the mouse is moving within the radius of the last move,
|
2012-03-03 16:31:46 +00:00
|
|
|
* don't update the mouse position. This allows sharp turns. */
|
2013-03-15 09:19:41 +00:00
|
|
|
if (dx * dx + dy * dy < radius * radius)
|
2009-08-19 21:24:52 +00:00
|
|
|
return 0;
|
|
|
|
|
2012-05-21 23:32:46 +00:00
|
|
|
output[0] = sample->mouse[0] * v + stroke->last_mouse_position[0] * u;
|
|
|
|
output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u;
|
2013-05-18 11:25:24 +00:00
|
|
|
*outpressure = sample->pressure * v + stroke->last_pressure * u;
|
2009-08-19 21:24:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
static float paint_space_stroke_spacing(const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure)
|
2009-08-19 21:24:52 +00:00
|
|
|
{
|
2013-05-18 11:25:24 +00:00
|
|
|
/* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel
|
|
|
|
* causing very high step sizes, hanging blender [#32381] */
|
|
|
|
const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure);
|
|
|
|
float spacing = stroke->brush->spacing;
|
2013-03-13 03:46:22 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
/* apply spacing pressure */
|
|
|
|
if (stroke->brush->flag & BRUSH_SPACING_PRESSURE)
|
2013-08-01 17:14:20 +00:00
|
|
|
spacing = spacing * (1.5f - spacing_pressure);
|
2009-08-19 21:24:52 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
/* stroke system is used for 2d paint too, so we need to account for
|
|
|
|
* the fact that brush can be scaled there. */
|
|
|
|
spacing *= stroke->zoom_2d;
|
2013-05-18 09:44:30 +00:00
|
|
|
|
2013-08-01 17:14:20 +00:00
|
|
|
return max_ff(1.0, size_clamp * spacing / 50.0f);
|
2013-05-18 11:25:24 +00:00
|
|
|
}
|
2013-05-18 09:44:30 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
static float paint_space_stroke_spacing_variable(const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length)
|
|
|
|
{
|
|
|
|
if (BKE_brush_use_size_pressure(scene, stroke->brush)) {
|
|
|
|
/* use pressure to modify size. set spacing so that at 100%, the circles
|
|
|
|
* are aligned nicely with no overlap. for this the spacing needs to be
|
|
|
|
* the average of the previous and next size. */
|
|
|
|
float s = paint_space_stroke_spacing(scene, stroke, 1.0f, pressure);
|
2013-05-18 11:37:49 +00:00
|
|
|
float q = s * dpressure / (2.0f * length);
|
|
|
|
float pressure_fac = (1.0f + q) / (1.0f - q);
|
2013-05-18 11:25:24 +00:00
|
|
|
|
|
|
|
float last_size_pressure = stroke->last_pressure;
|
2013-05-18 11:37:49 +00:00
|
|
|
float new_size_pressure = stroke->last_pressure * pressure_fac;
|
2013-05-18 11:25:24 +00:00
|
|
|
|
|
|
|
/* average spacing */
|
|
|
|
float last_spacing = paint_space_stroke_spacing(scene, stroke, last_size_pressure, pressure);
|
|
|
|
float new_spacing = paint_space_stroke_spacing(scene, stroke, new_size_pressure, pressure);
|
|
|
|
|
2013-05-18 11:37:49 +00:00
|
|
|
return 0.5f * (last_spacing + new_spacing);
|
2013-05-18 11:25:24 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* no size pressure */
|
|
|
|
return paint_space_stroke_spacing(scene, stroke, 1.0f, pressure);
|
|
|
|
}
|
|
|
|
}
|
2013-05-18 09:44:30 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
/* For brushes with stroke spacing enabled, moves mouse in steps
|
|
|
|
* towards the final mouse location. */
|
|
|
|
static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mouse[2], float final_pressure)
|
|
|
|
{
|
|
|
|
const Scene *scene = CTX_data_scene(C);
|
|
|
|
PaintStroke *stroke = op->customdata;
|
|
|
|
PaintMode mode = BKE_paintmode_get_active_from_context(C);
|
|
|
|
int cnt = 0;
|
2013-05-18 09:44:30 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
if (paint_space_stroke_enabled(stroke->brush, mode)) {
|
|
|
|
float pressure, dpressure;
|
|
|
|
float mouse[2], dmouse[2];
|
|
|
|
float length;
|
2013-05-18 09:44:30 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position);
|
2013-05-18 09:44:30 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
pressure = stroke->last_pressure;
|
|
|
|
dpressure = final_pressure - stroke->last_pressure;
|
2013-03-10 18:46:31 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
length = normalize_v2(dmouse);
|
2013-03-10 00:58:09 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
while (length > 0.0f) {
|
|
|
|
float spacing = paint_space_stroke_spacing_variable(scene, stroke, pressure, dpressure, length);
|
|
|
|
|
|
|
|
if (length >= spacing) {
|
2013-05-18 11:37:49 +00:00
|
|
|
mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
|
|
|
|
mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing;
|
|
|
|
pressure = stroke->last_pressure + (spacing / length) * dpressure;
|
2013-03-10 18:46:31 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
paint_brush_stroke_add_step(C, op, mouse, pressure);
|
2010-07-14 14:11:03 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
length -= spacing;
|
|
|
|
pressure = stroke->last_pressure;
|
|
|
|
dpressure = final_pressure - stroke->last_pressure;
|
2009-08-19 21:24:52 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
2009-08-19 21:24:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**** Public API ****/
|
|
|
|
|
2009-10-27 19:53:34 +00:00
|
|
|
PaintStroke *paint_stroke_new(bContext *C,
|
2012-03-28 03:47:33 +00:00
|
|
|
StrokeGetLocation get_location,
|
|
|
|
StrokeTestStart test_start,
|
|
|
|
StrokeUpdateStep update_step,
|
2013-05-15 14:37:05 +00:00
|
|
|
StrokeRedraw redraw,
|
2012-03-28 03:47:33 +00:00
|
|
|
StrokeDone done, int event_type)
|
2009-08-19 21:24:52 +00:00
|
|
|
{
|
|
|
|
PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
|
|
|
|
|
2013-04-16 15:02:41 +00:00
|
|
|
Brush *br = stroke->brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
|
2009-08-19 21:24:52 +00:00
|
|
|
view3d_set_viewcontext(C, &stroke->vc);
|
2013-03-08 04:00:06 +00:00
|
|
|
if (stroke->vc.v3d)
|
2013-03-06 19:54:43 +00:00
|
|
|
view3d_get_transformation(stroke->vc.ar, stroke->vc.rv3d, stroke->vc.obact, &stroke->mats);
|
2009-08-19 21:24:52 +00:00
|
|
|
|
2009-10-27 19:53:34 +00:00
|
|
|
stroke->get_location = get_location;
|
2009-08-19 21:24:52 +00:00
|
|
|
stroke->test_start = test_start;
|
|
|
|
stroke->update_step = update_step;
|
2013-05-15 14:37:05 +00:00
|
|
|
stroke->redraw = redraw;
|
2009-08-19 21:24:52 +00:00
|
|
|
stroke->done = done;
|
2012-03-28 03:47:33 +00:00
|
|
|
stroke->event_type = event_type; /* for modal, return event */
|
2011-03-01 16:26:37 +00:00
|
|
|
|
2013-04-23 00:32:51 +00:00
|
|
|
BKE_paint_set_overlay_override(br->overlay_flags);
|
2013-04-16 15:02:41 +00:00
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
return stroke;
|
|
|
|
}
|
|
|
|
|
2012-05-03 04:11:53 +00:00
|
|
|
void paint_stroke_data_free(struct wmOperator *op)
|
2010-01-09 17:15:02 +00:00
|
|
|
{
|
2013-04-23 00:32:51 +00:00
|
|
|
BKE_paint_set_overlay_override(0);
|
2012-05-03 04:11:53 +00:00
|
|
|
MEM_freeN(op->customdata);
|
2012-05-06 22:12:26 +00:00
|
|
|
op->customdata = NULL;
|
2012-05-03 04:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void stroke_done(struct bContext *C, struct wmOperator *op)
|
|
|
|
{
|
|
|
|
struct PaintStroke *stroke = op->customdata;
|
|
|
|
|
2013-05-15 14:37:05 +00:00
|
|
|
if (stroke->stroke_started) {
|
|
|
|
if (stroke->redraw)
|
|
|
|
stroke->redraw(C, stroke, true);
|
|
|
|
|
|
|
|
if (stroke->done)
|
|
|
|
stroke->done(C, stroke);
|
|
|
|
}
|
2012-05-03 04:11:53 +00:00
|
|
|
|
|
|
|
if (stroke->timer) {
|
|
|
|
WM_event_remove_timer(
|
|
|
|
CTX_wm_manager(C),
|
|
|
|
CTX_wm_window(C),
|
|
|
|
stroke->timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stroke->smooth_stroke_cursor)
|
|
|
|
WM_paint_cursor_end(CTX_wm_manager(C), stroke->smooth_stroke_cursor);
|
|
|
|
|
|
|
|
paint_stroke_data_free(op);
|
2011-01-15 14:48:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
|
2013-03-13 03:46:22 +00:00
|
|
|
bool paint_space_stroke_enabled(Brush *br, PaintMode mode)
|
2011-01-15 14:48:44 +00:00
|
|
|
{
|
2013-03-13 03:46:22 +00:00
|
|
|
return (br->flag & BRUSH_SPACE) && paint_supports_dynamic_size(br, mode);
|
2013-01-16 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
2013-04-15 14:55:36 +00:00
|
|
|
static bool sculpt_is_grab_tool(Brush *br)
|
|
|
|
{
|
|
|
|
return ELEM4(br->sculpt_tool,
|
2013-04-15 15:16:11 +00:00
|
|
|
SCULPT_TOOL_GRAB,
|
|
|
|
SCULPT_TOOL_THUMB,
|
|
|
|
SCULPT_TOOL_ROTATE,
|
|
|
|
SCULPT_TOOL_SNAKE_HOOK);
|
2013-04-15 14:55:36 +00:00
|
|
|
}
|
|
|
|
|
2013-01-16 19:59:55 +00:00
|
|
|
/* return true if the brush size can change during paint (normally used for pressure) */
|
2013-03-13 03:46:22 +00:00
|
|
|
bool paint_supports_dynamic_size(Brush *br, PaintMode mode)
|
2013-01-16 19:59:55 +00:00
|
|
|
{
|
2013-03-13 05:23:53 +00:00
|
|
|
if (br->flag & BRUSH_ANCHORED)
|
2013-03-13 03:46:22 +00:00
|
|
|
return false;
|
|
|
|
|
2013-03-13 05:23:53 +00:00
|
|
|
switch (mode) {
|
2013-03-13 03:46:22 +00:00
|
|
|
case PAINT_SCULPT:
|
2013-04-15 14:55:36 +00:00
|
|
|
if (sculpt_is_grab_tool(br))
|
2013-03-14 05:52:30 +00:00
|
|
|
return false;
|
2013-07-21 08:16:37 +00:00
|
|
|
break;
|
2013-03-13 03:46:22 +00:00
|
|
|
default:
|
2013-07-21 08:16:37 +00:00
|
|
|
break;
|
2013-03-14 05:52:30 +00:00
|
|
|
}
|
2013-03-13 03:46:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-14 02:27:36 +00:00
|
|
|
bool paint_supports_smooth_stroke(Brush *br, PaintMode mode)
|
|
|
|
{
|
2013-03-16 14:42:05 +00:00
|
|
|
if (!(br->flag & BRUSH_SMOOTH_STROKE) ||
|
|
|
|
(br->flag & BRUSH_ANCHORED) ||
|
|
|
|
(br->flag & BRUSH_RESTORE_MESH))
|
2013-03-14 05:52:30 +00:00
|
|
|
{
|
2013-03-14 02:27:36 +00:00
|
|
|
return false;
|
2013-03-14 05:52:30 +00:00
|
|
|
}
|
2013-03-14 02:27:36 +00:00
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case PAINT_SCULPT:
|
2013-04-15 14:55:36 +00:00
|
|
|
if (sculpt_is_grab_tool(br))
|
2013-03-14 02:27:36 +00:00
|
|
|
return false;
|
2013-07-21 08:16:37 +00:00
|
|
|
break;
|
2013-03-14 02:27:36 +00:00
|
|
|
default:
|
2013-07-21 08:16:37 +00:00
|
|
|
break;
|
2013-03-14 05:52:30 +00:00
|
|
|
}
|
2013-03-14 02:27:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-13 03:46:22 +00:00
|
|
|
/* return true if the brush size can change during paint (normally used for pressure) */
|
2013-03-14 02:27:36 +00:00
|
|
|
bool paint_supports_dynamic_tex_coords(Brush *br, PaintMode mode)
|
2013-03-13 03:46:22 +00:00
|
|
|
{
|
2013-03-13 05:23:53 +00:00
|
|
|
if (br->flag & BRUSH_ANCHORED)
|
2013-03-13 03:46:22 +00:00
|
|
|
return false;
|
|
|
|
|
2013-03-13 05:23:53 +00:00
|
|
|
switch (mode) {
|
2013-03-13 03:46:22 +00:00
|
|
|
case PAINT_SCULPT:
|
2013-04-15 14:55:36 +00:00
|
|
|
if (sculpt_is_grab_tool(br))
|
2013-03-13 05:23:53 +00:00
|
|
|
return false;
|
2013-07-21 08:16:37 +00:00
|
|
|
break;
|
2013-03-13 03:46:22 +00:00
|
|
|
default:
|
2013-07-19 16:44:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-03-13 03:46:22 +00:00
|
|
|
return true;
|
2010-01-09 17:15:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-03 04:11:53 +00:00
|
|
|
#define PAINT_STROKE_MODAL_CANCEL 1
|
|
|
|
|
|
|
|
/* called in paint_ops.c, on each regeneration of keymaps */
|
|
|
|
struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf)
|
|
|
|
{
|
|
|
|
static struct EnumPropertyItem modal_items[] = {
|
|
|
|
{PAINT_STROKE_MODAL_CANCEL, "CANCEL", 0,
|
|
|
|
"Cancel",
|
|
|
|
"Cancel and undo a stroke in progress"},
|
|
|
|
|
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
2012-05-06 22:12:26 +00:00
|
|
|
static const char *name = "Paint Stroke Modal";
|
2012-05-03 04:11:53 +00:00
|
|
|
|
2012-05-06 22:12:26 +00:00
|
|
|
struct wmKeyMap *keymap = WM_modalkeymap_get(keyconf, name);
|
2012-05-03 04:11:53 +00:00
|
|
|
|
|
|
|
/* this function is called for each spacetype, only needs to add map once */
|
|
|
|
if (!keymap) {
|
2012-05-06 22:12:26 +00:00
|
|
|
keymap = WM_modalkeymap_add(keyconf, name, modal_items);
|
2012-05-03 04:11:53 +00:00
|
|
|
|
|
|
|
/* items for modal map */
|
|
|
|
WM_modalkeymap_add_item(
|
|
|
|
keymap, ESCKEY, KM_PRESS, KM_ANY, 0, PAINT_STROKE_MODAL_CANCEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return keymap;
|
|
|
|
}
|
|
|
|
|
2012-05-21 23:32:46 +00:00
|
|
|
static void paint_stroke_add_sample(const Paint *paint,
|
2012-06-06 14:48:39 +00:00
|
|
|
PaintStroke *stroke,
|
2013-05-18 11:25:24 +00:00
|
|
|
float x, float y, float pressure)
|
2012-05-21 23:32:46 +00:00
|
|
|
{
|
|
|
|
PaintSample *sample = &stroke->samples[stroke->cur_sample];
|
|
|
|
int max_samples = MIN2(PAINT_MAX_INPUT_SAMPLES,
|
2012-06-06 14:48:39 +00:00
|
|
|
MAX2(paint->num_input_samples, 1));
|
2012-05-21 23:32:46 +00:00
|
|
|
|
|
|
|
sample->mouse[0] = x;
|
|
|
|
sample->mouse[1] = y;
|
2013-05-18 11:25:24 +00:00
|
|
|
sample->pressure = pressure;
|
2012-05-21 23:32:46 +00:00
|
|
|
|
|
|
|
stroke->cur_sample++;
|
|
|
|
if (stroke->cur_sample >= max_samples)
|
|
|
|
stroke->cur_sample = 0;
|
|
|
|
if (stroke->num_samples < max_samples)
|
|
|
|
stroke->num_samples++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void paint_stroke_sample_average(const PaintStroke *stroke,
|
2012-06-06 14:48:39 +00:00
|
|
|
PaintSample *average)
|
2012-05-21 23:32:46 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
memset(average, 0, sizeof(*average));
|
|
|
|
|
|
|
|
BLI_assert(stroke->num_samples > 0);
|
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
for (i = 0; i < stroke->num_samples; i++) {
|
2012-05-21 23:32:46 +00:00
|
|
|
add_v2_v2(average->mouse, stroke->samples[i].mouse);
|
2013-05-18 11:25:24 +00:00
|
|
|
average->pressure += stroke->samples[i].pressure;
|
|
|
|
}
|
2012-05-21 23:32:46 +00:00
|
|
|
|
|
|
|
mul_v2_fl(average->mouse, 1.0f / stroke->num_samples);
|
2013-05-18 11:25:24 +00:00
|
|
|
average->pressure /= stroke->num_samples;
|
2012-05-21 23:32:46 +00:00
|
|
|
|
|
|
|
/*printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);*/
|
|
|
|
}
|
|
|
|
|
2013-03-13 09:03:46 +00:00
|
|
|
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
2009-08-19 21:24:52 +00:00
|
|
|
{
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
Paint *p = BKE_paint_get_active_from_context(C);
|
|
|
|
PaintMode mode = BKE_paintmode_get_active_from_context(C);
|
2009-08-19 21:24:52 +00:00
|
|
|
PaintStroke *stroke = op->customdata;
|
2012-05-21 23:32:46 +00:00
|
|
|
PaintSample sample_average;
|
2009-08-19 21:24:52 +00:00
|
|
|
float mouse[2];
|
2013-05-18 15:24:25 +00:00
|
|
|
bool first_dab = false;
|
|
|
|
bool first_modal = false;
|
2013-03-15 09:19:41 +00:00
|
|
|
float zoomx, zoomy;
|
2013-05-15 14:37:05 +00:00
|
|
|
bool redraw = false;
|
2013-05-18 09:44:30 +00:00
|
|
|
float pressure;
|
2009-08-21 00:46:36 +00:00
|
|
|
|
2013-05-18 11:25:24 +00:00
|
|
|
/* see if tablet affects event */
|
|
|
|
pressure = event_tablet_data(event, &stroke->pen_flip);
|
|
|
|
|
|
|
|
paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure);
|
2012-05-21 23:32:46 +00:00
|
|
|
paint_stroke_sample_average(stroke, &sample_average);
|
|
|
|
|
2013-03-15 09:19:41 +00:00
|
|
|
get_imapaint_zoom(C, &zoomx, &zoomy);
|
|
|
|
stroke->zoom_2d = max_ff(zoomx, zoomy);
|
|
|
|
|
2012-10-20 20:20:02 +00:00
|
|
|
/* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
|
|
|
|
* this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
|
|
|
|
* since the 2D deltas are zero -- code in this file needs to be updated to use the
|
|
|
|
* post-NDOF_MOTION MOUSEMOVE */
|
2011-07-24 08:02:42 +00:00
|
|
|
if (event->type == NDOF_MOTION)
|
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
|
|
|
2013-05-18 15:24:25 +00:00
|
|
|
/* one time initialization */
|
2013-05-23 20:20:24 +00:00
|
|
|
if (!stroke->stroke_init) {
|
2013-05-18 15:24:25 +00:00
|
|
|
stroke->smooth_stroke_cursor =
|
|
|
|
WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke);
|
|
|
|
|
|
|
|
stroke->stroke_init = true;
|
|
|
|
first_modal = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* one time stroke initialization */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (!stroke->stroke_started) {
|
2013-05-18 11:25:24 +00:00
|
|
|
stroke->last_pressure = sample_average.pressure;
|
2013-05-18 15:24:25 +00:00
|
|
|
copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
|
2012-05-21 23:32:46 +00:00
|
|
|
stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
|
2012-12-28 01:36:00 +00:00
|
|
|
BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */
|
2009-08-20 05:13:07 +00:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
if (stroke->stroke_started) {
|
|
|
|
if (stroke->brush->flag & BRUSH_AIRBRUSH)
|
2009-10-20 13:58:53 +00:00
|
|
|
stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate);
|
2009-08-21 00:46:36 +00:00
|
|
|
|
2013-05-18 15:24:25 +00:00
|
|
|
first_dab = true;
|
|
|
|
}
|
2009-08-19 21:24:52 +00:00
|
|
|
}
|
|
|
|
|
2012-05-03 04:11:53 +00:00
|
|
|
/* Cancel */
|
|
|
|
if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) {
|
|
|
|
if (op->type->cancel)
|
|
|
|
return op->type->cancel(C, op);
|
|
|
|
else
|
|
|
|
return paint_stroke_cancel(C, op);
|
|
|
|
}
|
2009-11-04 20:15:17 +00:00
|
|
|
|
2013-05-18 15:24:25 +00:00
|
|
|
if (event->type == stroke->event_type && event->val == KM_RELEASE && !first_modal) {
|
2012-05-03 04:11:53 +00:00
|
|
|
stroke_done(C, op);
|
2009-11-04 20:15:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
2013-05-18 15:24:25 +00:00
|
|
|
else if (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) ||
|
2011-12-24 02:37:42 +00:00
|
|
|
(event->type == TIMER && (event->customdata == stroke->timer)) )
|
|
|
|
{
|
2013-05-18 15:24:25 +00:00
|
|
|
if (paint_smooth_stroke(stroke, mouse, &pressure, &sample_average, mode)) {
|
|
|
|
if (stroke->stroke_started) {
|
2013-03-13 03:46:22 +00:00
|
|
|
if (paint_space_stroke_enabled(stroke->brush, mode)) {
|
2013-05-18 09:44:30 +00:00
|
|
|
if (paint_space_stroke(C, op, mouse, pressure))
|
2013-05-15 14:37:05 +00:00
|
|
|
redraw = true;
|
Sculpt: Multithreading & PBVH Changes
* Sculpting, normal update and bounding box code is now multithreaded
using OpenMP.
* Fix a number of update issues: normals on node boundaries, outdated
bounding boxes, partial redraw, .. . There's probably still a few
left, but should be better now.
* Clicking once now does a single paint instead of two (was also
painting on mouse up event).
* Smooth shading now is enabled for the full mesh when the first face
uses it (so it can be tested at least).
Implementation Notes:
* PBVH search can now be done either using a callback or bt gathering the
nodes in an array. The latter makes multithreading with OpenMP easier.
* Normals update code is now inside PBVH, was doing it per node before but
should do all faces first and only then vertices.
* Instead of using search modes + 1 modified flag, now nodes get 4 flags
to indicate what needs to be updated for them, found that this makes it
easier for me to understand the code and fix update bugs.
* PBVHNode is now exposed as an abstract type, I think this makes it more
clear what is happening than having it's data passed as part of callback
functions.
* Active_verts list was replaced by looping over nodes and the vertices
inside them. However the grab brush still uses the active_verts system,
will fix that later.
* Some micro-optimizations, like avoiding a few multiplications/divisions,
using local variables instead of pointers, or looping over fewer vertices
to update the bounding boxes.
2009-11-02 18:47:03 +00:00
|
|
|
}
|
2010-07-14 14:11:03 +00:00
|
|
|
else {
|
2013-05-18 09:44:30 +00:00
|
|
|
paint_brush_stroke_add_step(C, op, mouse, pressure);
|
2013-05-15 14:37:05 +00:00
|
|
|
redraw = true;
|
2010-07-14 14:11:03 +00:00
|
|
|
}
|
2009-08-19 21:24:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Sculpt: Multithreading & PBVH Changes
* Sculpting, normal update and bounding box code is now multithreaded
using OpenMP.
* Fix a number of update issues: normals on node boundaries, outdated
bounding boxes, partial redraw, .. . There's probably still a few
left, but should be better now.
* Clicking once now does a single paint instead of two (was also
painting on mouse up event).
* Smooth shading now is enabled for the full mesh when the first face
uses it (so it can be tested at least).
Implementation Notes:
* PBVH search can now be done either using a callback or bt gathering the
nodes in an array. The latter makes multithreading with OpenMP easier.
* Normals update code is now inside PBVH, was doing it per node before but
should do all faces first and only then vertices.
* Instead of using search modes + 1 modified flag, now nodes get 4 flags
to indicate what needs to be updated for them, found that this makes it
easier for me to understand the code and fix update bugs.
* PBVHNode is now exposed as an abstract type, I think this makes it more
clear what is happening than having it's data passed as part of callback
functions.
* Active_verts list was replaced by looping over nodes and the vertices
inside them. However the grab brush still uses the active_verts system,
will fix that later.
* Some micro-optimizations, like avoiding a few multiplications/divisions,
using local variables instead of pointers, or looping over fewer vertices
to update the bounding boxes.
2009-11-02 18:47:03 +00:00
|
|
|
|
2011-12-24 02:37:42 +00:00
|
|
|
/* we want the stroke to have the first daub at the start location
|
|
|
|
* instead of waiting till we have moved the space distance */
|
2013-05-18 15:24:25 +00:00
|
|
|
if (first_dab &&
|
2013-03-13 03:46:22 +00:00
|
|
|
paint_space_stroke_enabled(stroke->brush, mode) &&
|
2012-03-28 03:47:33 +00:00
|
|
|
!(stroke->brush->flag & BRUSH_ANCHORED) &&
|
|
|
|
!(stroke->brush->flag & BRUSH_SMOOTH_STROKE))
|
2010-07-14 14:11:03 +00:00
|
|
|
{
|
2013-05-18 09:44:30 +00:00
|
|
|
paint_brush_stroke_add_step(C, op, mouse, pressure);
|
2013-05-15 14:37:05 +00:00
|
|
|
redraw = true;
|
2010-07-14 14:11:03 +00:00
|
|
|
}
|
2013-05-15 14:37:05 +00:00
|
|
|
|
|
|
|
/* do updates for redraw. if event is inbetween mousemove there are more
|
|
|
|
* coming, so postpone potentially slow redraw updates until all are done */
|
|
|
|
if (event->type != INBETWEEN_MOUSEMOVE)
|
|
|
|
if (redraw && stroke->redraw)
|
|
|
|
stroke->redraw(C, stroke, false);
|
2010-07-14 14:11:03 +00:00
|
|
|
|
Sculpt: Multithreading & PBVH Changes
* Sculpting, normal update and bounding box code is now multithreaded
using OpenMP.
* Fix a number of update issues: normals on node boundaries, outdated
bounding boxes, partial redraw, .. . There's probably still a few
left, but should be better now.
* Clicking once now does a single paint instead of two (was also
painting on mouse up event).
* Smooth shading now is enabled for the full mesh when the first face
uses it (so it can be tested at least).
Implementation Notes:
* PBVH search can now be done either using a callback or bt gathering the
nodes in an array. The latter makes multithreading with OpenMP easier.
* Normals update code is now inside PBVH, was doing it per node before but
should do all faces first and only then vertices.
* Instead of using search modes + 1 modified flag, now nodes get 4 flags
to indicate what needs to be updated for them, found that this makes it
easier for me to understand the code and fix update bugs.
* PBVHNode is now exposed as an abstract type, I think this makes it more
clear what is happening than having it's data passed as part of callback
functions.
* Active_verts list was replaced by looping over nodes and the vertices
inside them. However the grab brush still uses the active_verts system,
will fix that later.
* Some micro-optimizations, like avoiding a few multiplications/divisions,
using local variables instead of pointers, or looping over fewer vertices
to update the bounding boxes.
2009-11-02 18:47:03 +00:00
|
|
|
return OPERATOR_RUNNING_MODAL;
|
2009-08-19 21:24:52 +00:00
|
|
|
}
|
|
|
|
|
2009-08-20 05:44:32 +00:00
|
|
|
int paint_stroke_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
PaintStroke *stroke = op->customdata;
|
|
|
|
|
2011-05-04 03:34:55 +00:00
|
|
|
/* only when executed for the first time */
|
2012-03-24 06:38:07 +00:00
|
|
|
if (stroke->stroke_started == 0) {
|
2011-05-04 03:34:55 +00:00
|
|
|
/* XXX stroke->last_mouse_position is unset, this may cause problems */
|
|
|
|
stroke->test_start(C, op, NULL);
|
2012-03-28 03:47:33 +00:00
|
|
|
stroke->stroke_started = 1;
|
2011-05-04 03:34:55 +00:00
|
|
|
}
|
|
|
|
|
2012-04-30 16:22:40 +00:00
|
|
|
RNA_BEGIN (op->ptr, itemptr, "stroke")
|
|
|
|
{
|
2009-08-20 05:44:32 +00:00
|
|
|
stroke->update_step(C, stroke, &itemptr);
|
|
|
|
}
|
|
|
|
RNA_END;
|
|
|
|
|
2012-05-03 04:11:53 +00:00
|
|
|
stroke_done(C, op);
|
2009-08-20 05:44:32 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2011-06-06 11:04:54 +00:00
|
|
|
int paint_stroke_cancel(bContext *C, wmOperator *op)
|
|
|
|
{
|
2012-05-03 04:11:53 +00:00
|
|
|
stroke_done(C, op);
|
2011-06-06 11:04:54 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
2009-08-19 21:24:52 +00:00
|
|
|
ViewContext *paint_stroke_view_context(PaintStroke *stroke)
|
|
|
|
{
|
|
|
|
return &stroke->vc;
|
|
|
|
}
|
|
|
|
|
2009-08-20 05:13:07 +00:00
|
|
|
void *paint_stroke_mode_data(struct PaintStroke *stroke)
|
|
|
|
{
|
|
|
|
return stroke->mode_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
|
|
|
|
{
|
|
|
|
stroke->mode_data = mode_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
int paint_poll(bContext *C)
|
|
|
|
{
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
Paint *p = BKE_paint_get_active_from_context(C);
|
2009-08-20 05:13:07 +00:00
|
|
|
Object *ob = CTX_data_active_object(C);
|
2012-09-23 05:33:23 +00:00
|
|
|
ScrArea *sa = CTX_wm_area(C);
|
|
|
|
ARegion *ar = CTX_wm_region(C);
|
2009-08-20 05:13:07 +00:00
|
|
|
|
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
|
|
|
return p && ob && BKE_paint_brush(p) &&
|
2012-09-23 05:33:23 +00:00
|
|
|
(sa && sa->spacetype == SPACE_VIEW3D) &&
|
|
|
|
(ar && ar->regiontype == RGN_TYPE_WINDOW);
|
2009-08-20 05:13:07 +00:00
|
|
|
}
|