Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt41
-rw-r--r--source/blender/editors/sculpt_paint/Makefile2
-rw-r--r--source/blender/editors/sculpt_paint/SConscript10
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c207
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h9
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c244
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c831
-rw-r--r--source/blender/editors/sculpt_paint/paint_undo.c36
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c94
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c3102
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h48
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c302
13 files changed, 3791 insertions, 1142 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
new file mode 100644
index 00000000000..f0493d8e2d8
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -0,0 +1,41 @@
+# $Id: CMakeLists.txt 12931 2007-12-17 18:20:48Z theeth $
+# ***** 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.
+#
+# Contributor(s): Jacques Beaurain.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+FILE(GLOB SRC *.c)
+
+SET(INC
+ ../../blenkernel
+ ../../imbuf
+ ../../gpu
+ ../../blenlib
+ ../include
+ ../../../../intern/guardedalloc
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../render/extern/include
+)
+
+IF(WIN32)
+ SET(INC ${INC} ${PTHREADS_INC})
+ENDIF(WIN32)
+
+BLENDERLIB(bf_editor_sculpt_paint "${SRC}" "${INC}")
diff --git a/source/blender/editors/sculpt_paint/Makefile b/source/blender/editors/sculpt_paint/Makefile
index 012a39b8d25..f9f39fea7eb 100644
--- a/source/blender/editors/sculpt_paint/Makefile
+++ b/source/blender/editors/sculpt_paint/Makefile
@@ -15,7 +15,7 @@
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2007 Blender Foundation
# All rights reserved.
diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript
index 472ba361059..3d9a5144c93 100644
--- a/source/blender/editors/sculpt_paint/SConscript
+++ b/source/blender/editors/sculpt_paint/SConscript
@@ -3,6 +3,8 @@ Import ('env')
sources = env.Glob('*.c')
+defs = []
+
incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../render/extern/include'
@@ -12,7 +14,11 @@ if env['OURPLATFORM'] == 'linux2':
cflags='-pthread'
incs += ' ../../../extern/binreloc/include'
+if env['OURPLATFORM'] == 'linuxcross':
+ if env['WITH_BF_OPENMP']:
+ incs += ' ' + env['BF_OPENMP_INC']
+
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
- incs += ' ' + env['BF_PTHREADS_INC']
+ incs += ' ' + env['BF_PTHREADS_INC']
-env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), [], libtype=['core'], priority=[40] )
+env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), defines=defs, libtype=['core'], priority=[40] )
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index df2e4032257..418bac69974 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -54,18 +54,16 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_brush_types.h"
#include "BKE_context.h"
#include "BKE_idprop.h"
#include "BKE_object.h"
#include "BKE_brush.h"
-#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
-#include "BKE_node.h"
#include "BKE_paint.h"
-#include "BKE_utildefines.h"
#include "BKE_DerivedMesh.h"
#include "BKE_report.h"
#include "BKE_depsgraph.h"
@@ -641,8 +639,8 @@ static int project_paint_PickColor(const ProjPaintState *ps, float pt[2], float
}
}
else {
- xi = (uv[0]*ibuf->x) + 0.5f;
- yi = (uv[1]*ibuf->y) + 0.5f;
+ xi = (int)((uv[0]*ibuf->x) + 0.5f);
+ yi = (int)((uv[1]*ibuf->y) + 0.5f);
//if (xi<0 || xi>=ibuf->x || yi<0 || yi>=ibuf->y) return 0;
@@ -1053,15 +1051,15 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl
* This is incorrect. Its already given radians but without it wont work.
* need to look into a fix - campbell */
if (is_quad) {
- a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * (M_PI/180.0f));
- a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f));
- a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f));
- a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * (M_PI/180.0f));
+ a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * ((float)M_PI/180.0f));
+ a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f));
+ a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f));
+ a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * ((float)M_PI/180.0f));
}
else {
- a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * (M_PI/180.0f));
- a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f));
- a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f));
+ a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * ((float)M_PI/180.0f));
+ a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f));
+ a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f));
}
if (is_quad) {
@@ -1197,7 +1195,7 @@ static void screen_px_from_persp(
w[2] *= wtot_inv;
}
else {
- w[0] = w[1] = w[2] = 1.0/3.0; /* dummy values for zero area face */
+ w[0] = w[1] = w[2] = 1.0f/3.0f; /* dummy values for zero area face */
}
/* done re-weighting */
@@ -1334,7 +1332,7 @@ float project_paint_uvpixel_mask(
// This only works when the opacity dosnt change while painting, stylus pressure messes with this
// so dont use it.
- // if (ps->is_airbrush==0) mask *= ps->brush->alpha;
+ // if (ps->is_airbrush==0) mask *= brush_alpha(ps->brush);
return mask;
}
@@ -1444,7 +1442,7 @@ static ProjPixel *project_paint_uvpixel_init(
sub_v2_v2v2(co, projPixel->projCoSS, (float *)ps->cloneOffset);
/* no need to initialize the bucket, we're only checking buckets faces and for this
- * the faces are alredy initialized in project_paint_delayed_face_init(...) */
+ * the faces are already initialized in project_paint_delayed_face_init(...) */
if (ibuf->rect_float) {
if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) {
((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; /* zero alpha - ignore */
@@ -1642,10 +1640,10 @@ static void scale_quad(float insetCos[4][3], float *origCos[4], const float inse
mul_v3_fl(insetCos[2], inset);
mul_v3_fl(insetCos[3], inset);
- add_v3_v3v3(insetCos[0], insetCos[0], cent);
- add_v3_v3v3(insetCos[1], insetCos[1], cent);
- add_v3_v3v3(insetCos[2], insetCos[2], cent);
- add_v3_v3v3(insetCos[3], insetCos[3], cent);
+ add_v3_v3(insetCos[0], cent);
+ add_v3_v3(insetCos[1], cent);
+ add_v3_v3(insetCos[2], cent);
+ add_v3_v3(insetCos[3], cent);
}
@@ -1664,9 +1662,9 @@ static void scale_tri(float insetCos[4][3], float *origCos[4], const float inset
mul_v3_fl(insetCos[1], inset);
mul_v3_fl(insetCos[2], inset);
- add_v3_v3v3(insetCos[0], insetCos[0], cent);
- add_v3_v3v3(insetCos[1], insetCos[1], cent);
- add_v3_v3v3(insetCos[2], insetCos[2], cent);
+ add_v3_v3(insetCos[0], cent);
+ add_v3_v3(insetCos[1], cent);
+ add_v3_v3(insetCos[2], cent);
}
@@ -1694,7 +1692,7 @@ static float Vec2Lenf_nosqrt_other(const float *v1, const float v2_1, const floa
static int project_bucket_isect_circle(const int bucket_x, const int bucket_y, const float cent[2], const float radius_squared, rctf *bucket_bounds)
{
- /* Would normally to a simple intersection test, however we know the bounds of these 2 alredy intersect
+ /* Would normally to a simple intersection test, however we know the bounds of these 2 already intersect
* so we only need to test if the center is inside the vertical or horizontal bounds on either axis,
* this is even less work then an intersection test
*
@@ -2513,11 +2511,11 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
w[0]=w[1]=w[2]= 0.0;
if (side) {
w[fidx1?fidx1-1:0] = fac;
- w[fidx2?fidx2-1:0] = 1.0-fac;
+ w[fidx2?fidx2-1:0] = 1.0f-fac;
}
else {
w[fidx1] = fac;
- w[fidx2] = 1.0-fac;
+ w[fidx2] = 1.0f-fac;
}
#endif
}
@@ -2571,11 +2569,12 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
static void project_paint_bucket_bounds(const ProjPaintState *ps, const float min[2], const float max[2], int bucketMin[2], int bucketMax[2])
{
/* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */
- bucketMin[0] = (int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f; /* these offsets of 0.5 and 1.5 seem odd but they are correct */
- bucketMin[1] = (int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f;
+ /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f is always truncated to 1, is this really correct?? - jwilkins */
+ bucketMin[0] = (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); /* these offsets of 0.5 and 1.5 seem odd but they are correct */
+ bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f);
- bucketMax[0] = (int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f;
- bucketMax[1] = (int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f;
+ bucketMax[0] = (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f);
+ bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f);
/* incase the rect is outside the mesh 2d bounds */
CLAMP(bucketMin[0], 0, ps->buckets_x);
@@ -2808,6 +2807,8 @@ static void project_paint_begin(ProjPaintState *ps)
MVert *mv;
MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */
+
+ const int diameter= 2*brush_size(ps->brush);
/* ---- end defines ---- */
@@ -2973,7 +2974,7 @@ static void project_paint_begin(ProjPaintState *ps)
VECCOPY(ps->viewPos, viewinv[3]);
copy_m3_m4(mat, ps->ob->imat);
mul_m3_v3(mat, ps->viewPos);
- add_v3_v3v3(ps->viewPos, ps->viewPos, ps->ob->imat[3]);
+ add_v3_v3(ps->viewPos, ps->ob->imat[3]);
}
/* calculate vert screen coords
@@ -3029,27 +3030,27 @@ static void project_paint_begin(ProjPaintState *ps)
if(ps->source==PROJ_SRC_VIEW) {
#ifdef PROJ_DEBUG_WINCLIP
- CLAMP(ps->screenMin[0], -ps->brush->size, ps->winx + ps->brush->size);
- CLAMP(ps->screenMax[0], -ps->brush->size, ps->winx + ps->brush->size);
+ CLAMP(ps->screenMin[0], (float)(-diameter), (float)(ps->winx + diameter));
+ CLAMP(ps->screenMax[0], (float)(-diameter), (float)(ps->winx + diameter));
- CLAMP(ps->screenMin[1], -ps->brush->size, ps->winy + ps->brush->size);
- CLAMP(ps->screenMax[1], -ps->brush->size, ps->winy + ps->brush->size);
+ CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter));
+ CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter));
#endif
}
else { /* reprojection, use bounds */
ps->screenMin[0]= 0;
- ps->screenMax[0]= ps->winx;
+ ps->screenMax[0]= (float)(ps->winx);
ps->screenMin[1]= 0;
- ps->screenMax[1]= ps->winy;
+ ps->screenMax[1]= (float)(ps->winy);
}
/* only for convenience */
ps->screen_width = ps->screenMax[0] - ps->screenMin[0];
ps->screen_height = ps->screenMax[1] - ps->screenMin[1];
- ps->buckets_x = (int)(ps->screen_width / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV));
- ps->buckets_y = (int)(ps->screen_height / (((float)ps->brush->size) / PROJ_BUCKET_BRUSH_DIV));
+ ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV));
+ ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV));
/* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */
@@ -3083,7 +3084,7 @@ static void project_paint_begin(ProjPaintState *ps)
ps->thread_tot = BLI_system_thread_count();
}
for (a=0; a<ps->thread_tot; a++) {
- ps->arena_mt[a] = BLI_memarena_new(1<<16);
+ ps->arena_mt[a] = BLI_memarena_new(1<<16, "project paint arena");
}
arena = ps->arena_mt[0];
@@ -3447,16 +3448,16 @@ static int project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2])
{
if(ps->source==PROJ_SRC_VIEW) {
float min_brush[2], max_brush[2];
- float size_half = ((float)ps->brush->size) * 0.5f;
+ const float radius = (float)brush_size(ps->brush);
/* so we dont have a bucket bounds that is way too small to paint into */
- // if (size_half < 1.0f) size_half = 1.0f; // this dosnt work yet :/
+ // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/
- min_brush[0] = mval_f[0] - size_half;
- min_brush[1] = mval_f[1] - size_half;
+ min_brush[0] = mval_f[0] - radius;
+ min_brush[1] = mval_f[1] - radius;
- max_brush[0] = mval_f[0] + size_half;
- max_brush[1] = mval_f[1] + size_half;
+ max_brush[0] = mval_f[0] + radius;
+ max_brush[1] = mval_f[1] + radius;
/* offset to make this a valid bucket index */
project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax);
@@ -3485,6 +3486,8 @@ static int project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2])
static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2])
{
+ const int diameter= 2*brush_size(ps->brush);
+
if (ps->thread_tot > 1)
BLI_lock_thread(LOCK_CUSTOM1);
@@ -3497,7 +3500,7 @@ static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf
project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds);
if ( (ps->source != PROJ_SRC_VIEW) ||
- project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, ps->brush->size * ps->brush->size, bucket_bounds)
+ project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, (float)(diameter*diameter), bucket_bounds)
) {
*bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x);
ps->context_bucket_x++;
@@ -3545,23 +3548,25 @@ static void blend_color_mix(unsigned char *cp, const unsigned char *cp1, const u
static void blend_color_mix_float(float *cp, const float *cp1, const float *cp2, const float fac)
{
- const float mfac= 1.0-fac;
+ const float mfac= 1.0f-fac;
cp[0]= mfac*cp1[0] + fac*cp2[0];
cp[1]= mfac*cp1[1] + fac*cp2[1];
cp[2]= mfac*cp1[2] + fac*cp2[2];
cp[3]= mfac*cp1[3] + fac*cp2[3];
}
-static void blend_color_mix_rgb(unsigned char *cp, const unsigned char *cp1, const unsigned char *cp2, const int fac)
+static void blend_color_mix_accum(unsigned char *cp, const unsigned char *cp1, const unsigned char *cp2, const int fac)
{
/* this and other blending modes previously used >>8 instead of /255. both
are not equivalent (>>8 is /256), and the former results in rounding
errors that can turn colors black fast after repeated blending */
const int mfac= 255-fac;
+ const int alpha= cp1[3] + ((fac * cp2[3]) / 255);
cp[0]= (mfac*cp1[0]+fac*cp2[0])/255;
cp[1]= (mfac*cp1[1]+fac*cp2[1])/255;
cp[2]= (mfac*cp1[2]+fac*cp2[2])/255;
+ cp[3]= alpha > 255 ? 255 : alpha;
}
static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask)
@@ -3682,7 +3687,6 @@ static void *do_projectpaint_thread(void *ph_v)
float rgba[4], alpha, dist_nosqrt, dist;
- float brush_size_sqared;
float falloff;
int bucket_index;
int is_floatbuf = 0;
@@ -3694,24 +3698,22 @@ static void *do_projectpaint_thread(void *ph_v)
float co[2];
float mask = 1.0f; /* airbrush wont use mask */
unsigned short mask_short;
- float size_half = ((float)ps->brush->size) * 0.5f;
+ const float radius= (float)brush_size(ps->brush);
+ const float radius_squared= radius*radius; /* avoid a square root with every dist comparison */
+
short lock_alpha= ELEM(ps->brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? 0 : ps->brush->flag & BRUSH_LOCK_ALPHA;
LinkNode *smearPixels = NULL;
LinkNode *smearPixels_f = NULL;
MemArena *smearArena = NULL; /* mem arena for this brush projection only */
-
if (tool==PAINT_TOOL_SMEAR) {
pos_ofs[0] = pos[0] - lastpos[0];
pos_ofs[1] = pos[1] - lastpos[1];
- smearArena = BLI_memarena_new(1<<16);
+ smearArena = BLI_memarena_new(1<<16, "paint smear arena");
}
- /* avoid a square root with every dist comparison */
- brush_size_sqared = ps->brush->size * ps->brush->size;
-
/* printf("brush bounds %d %d %d %d\n", bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */
while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) {
@@ -3732,7 +3734,8 @@ static void *do_projectpaint_thread(void *ph_v)
bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, projPixel->projCoSS[0], projPixel->projCoSS[1]);
if(projPixel->newColor.ch[3]) {
mask = ((float)projPixel->mask)/65535.0f;
- blend_color_mix_rgb(projPixel->pixel.ch_pt, projPixel->origColor.ch, projPixel->newColor.ch, (mask*projPixel->newColor.ch[3]));
+ blend_color_mix_accum(projPixel->pixel.ch_pt, projPixel->origColor.ch, projPixel->newColor.ch, (int)(mask*projPixel->newColor.ch[3]));
+
}
}
}
@@ -3746,9 +3749,11 @@ static void *do_projectpaint_thread(void *ph_v)
/*dist = len_v2v2(projPixel->projCoSS, pos);*/ /* correct but uses a sqrtf */
dist_nosqrt = Vec2Lenf_nosqrt(projPixel->projCoSS, pos);
- /*if (dist < s->brush->size) {*/ /* correct but uses a sqrtf */
- if (dist_nosqrt < brush_size_sqared && (dist=sqrtf(dist_nosqrt)) < size_half) {
- falloff = brush_curve_strength_clamp(ps->brush, dist, size_half);
+ /*if (dist < radius) {*/ /* correct but uses a sqrtf */
+ if (dist_nosqrt <= radius_squared) {
+ dist=sqrtf(dist_nosqrt);
+
+ falloff = brush_curve_strength_clamp(ps->brush, dist, radius);
if (falloff > 0.0f) {
if (ps->is_texbrush) {
@@ -3760,7 +3765,7 @@ static void *do_projectpaint_thread(void *ph_v)
if (ps->is_airbrush) {
/* for an aurbrush there is no real mask, so just multiply the alpha by it */
- alpha *= falloff * ps->brush->alpha;
+ alpha *= falloff * brush_alpha(ps->brush);
mask = ((float)projPixel->mask)/65535.0f;
}
else {
@@ -3768,7 +3773,7 @@ static void *do_projectpaint_thread(void *ph_v)
falloff = 1.0f - falloff;
falloff = 1.0f - (falloff * falloff);
- mask_short = projPixel->mask * (ps->brush->alpha * falloff);
+ mask_short = (unsigned short)(projPixel->mask * (brush_alpha(ps->brush) * falloff));
if (mask_short > projPixel->mask_max) {
mask = ((float)mask_short)/65535.0f;
projPixel->mask_max = mask_short;
@@ -3929,8 +3934,8 @@ static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, i
/* Use mouse coords as floats for projection painting */
float pos[2];
- pos[0] = mval_i[0];
- pos[1] = mval_i[1];
+ pos[0] = (float)(mval_i[0]);
+ pos[1] = (float)(mval_i[1]);
// we may want to use this later
// brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0);
@@ -4054,7 +4059,8 @@ static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo
{
float inrgb[3];
- if ((x >= ibuf->x) || (y >= ibuf->y)) {
+ // XXX: signed unsigned mismatch
+ if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) {
if (torus) imapaint_ibuf_get_set_rgb(ibuf, x, y, 1, 0, inrgb);
else return 0;
}
@@ -4515,7 +4521,7 @@ typedef struct PaintOperation {
int first;
int prevmouse[2];
float prev_pressure; /* need this since we dont get tablet events for pressure change */
- int brush_size_orig;
+ int orig_brush_size;
double starttime;
ViewContext vc;
@@ -4608,8 +4614,8 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps)
ps->normal_angle_inner= ps->normal_angle= settings->imapaint.normal_angle;
}
- ps->normal_angle_inner *= M_PI_2 / 90;
- ps->normal_angle *= M_PI_2 / 90;
+ ps->normal_angle_inner *= (float)(M_PI_2 / 90);
+ ps->normal_angle *= (float)(M_PI_2 / 90);
ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner;
if(ps->normal_angle_range <= 0.0f)
@@ -4648,7 +4654,7 @@ static int texture_paint_init(bContext *C, wmOperator *op)
if(pop->mode == PAINT_MODE_3D && (pop->s.tool == PAINT_TOOL_CLONE))
pop->s.tool = PAINT_TOOL_DRAW;
pop->s.blend = brush->blend;
- pop->brush_size_orig= brush->size;
+ pop->orig_brush_size= brush_size(brush);
if(pop->mode != PAINT_MODE_2D) {
pop->s.ob = OBACT;
@@ -4680,8 +4686,8 @@ static int texture_paint_init(bContext *C, wmOperator *op)
return 0;
/* Dont allow brush size below 2 */
- if (brush->size <= 1)
- brush->size = 2;
+ if (brush_size(brush) < 2)
+ brush_set_size(brush, 2);
/* allocate and initialize spacial data structures */
project_paint_begin(&pop->ps);
@@ -4691,7 +4697,7 @@ static int texture_paint_init(bContext *C, wmOperator *op)
}
settings->imapaint.flag |= IMAGEPAINT_DRAWING;
- undo_paint_push_begin(UNDO_PAINT_IMAGE, "Image Paint",
+ undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
image_undo_restore, image_undo_free);
/* create painter */
@@ -4708,8 +4714,8 @@ static void paint_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
int mouse[2], redraw;
RNA_float_get_array(itemptr, "mouse", mousef);
- mouse[0] = mousef[0];
- mouse[1] = mousef[1];
+ mouse[0] = (int)(mousef[0]);
+ mouse[1] = (int)(mousef[1]);
time= RNA_float_get(itemptr, "time");
pressure= RNA_float_get(itemptr, "pressure");
@@ -4751,7 +4757,7 @@ static void paint_exit(bContext *C, wmOperator *op)
brush_painter_free(pop->painter);
if(pop->mode == PAINT_MODE_3D_PROJECT) {
- pop->ps.brush->size = pop->brush_size_orig;
+ brush_set_size(pop->ps.brush, pop->orig_brush_size);
project_paint_end(&pop->ps);
}
@@ -4822,15 +4828,22 @@ static void paint_apply_event(bContext *C, wmOperator *op, wmEvent *event)
/* special exception here for too high pressure values on first touch in
windows for some tablets, then we just skip first touch .. */
- if ((pop->s.brush->flag & (BRUSH_ALPHA_PRESSURE|BRUSH_SIZE_PRESSURE|BRUSH_SPACING_PRESSURE)) && tablet && (pressure >= 0.99f))
+ if (tablet && (pressure >= 0.99f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || brush_use_alpha_pressure(pop->s.brush) || brush_use_size_pressure(pop->s.brush)))
+ return;
+
+ /* This can be removed once fixed properly in
+ 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 .0002 which is the sensitivity of the most sensitive pen tablet available*/
+ if (tablet && (pressure < .0002f) && ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || brush_use_alpha_pressure(pop->s.brush) || brush_use_size_pressure(pop->s.brush)))
return;
+
}
/* fill in stroke */
RNA_collection_add(op->ptr, "stroke", &itemptr);
- mousef[0] = mouse[0];
- mousef[1] = mouse[1];
+ mousef[0] = (float)(mouse[0]);
+ mousef[1] = (float)(mouse[1]);
RNA_float_set_array(&itemptr, "mouse", mousef);
RNA_float_set(&itemptr, "time", (float)(time - pop->starttime));
RNA_float_set(&itemptr, "pressure", pressure);
@@ -4872,6 +4885,7 @@ static int paint_modal(bContext *C, wmOperator *op, wmEvent *event)
paint_exit(C, op);
return OPERATOR_FINISHED;
case MOUSEMOVE:
+ case INBETWEEN_MOUSEMOVE:
paint_apply_event(C, op, event);
break;
case TIMER:
@@ -4933,9 +4947,14 @@ static int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
{
Brush *brush= image_paint_brush(C);
+ Paint *paint= paint_get_active(CTX_data_scene(C));
- if(brush) {
+ if(paint && brush) {
float zoomx, zoomy;
+
+ if(!(paint->flags & PAINT_SHOW_BRUSH))
+ return;
+
glPushMatrix();
glTranslatef((float)x, (float)y, 0.0f);
@@ -4943,13 +4962,13 @@ static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
if(get_imapaint_zoom(C, &zoomx, &zoomy))
glScalef(zoomx, zoomy, 1.0f);
- glColor4ub(255, 255, 255, 128);
+ glColor4f(brush->add_col[0], brush->add_col[1], brush->add_col[2], 0.5f);
glEnable( GL_LINE_SMOOTH );
glEnable(GL_BLEND);
- glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size*0.5f, 40);
+ glutil_draw_lined_arc(0, (float)(M_PI*2.0), (float)brush_size(brush), 40);
glDisable(GL_BLEND);
glDisable( GL_LINE_SMOOTH );
-
+
glPopMatrix();
}
}
@@ -4973,7 +4992,7 @@ static int paint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *eve
ToolSettings *ts = CTX_data_scene(C)->toolsettings;
get_imapaint_zoom(C, &zoom, &zoom);
toggle_paint_cursor(C, !ts->imapaint.paintcursor);
- brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5 * zoom);
+ brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), zoom);
return WM_radial_control_invoke(C, op, event);
}
@@ -4993,7 +5012,7 @@ static int paint_radial_control_exec(bContext *C, wmOperator *op)
int ret;
char str[256];
get_imapaint_zoom(C, &zoom, &zoom);
- ret = brush_radial_control_exec(op, brush, 2.0 / zoom);
+ ret = brush_radial_control_exec(op, brush, 1.0f / zoom);
WM_radial_control_string(op, str, 256);
WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush);
@@ -5300,14 +5319,14 @@ static int texture_paint_radial_control_invoke(bContext *C, wmOperator *op, wmEv
{
ToolSettings *ts = CTX_data_scene(C)->toolsettings;
toggle_paint_cursor(C, !ts->imapaint.paintcursor);
- brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5);
+ brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 1);
return WM_radial_control_invoke(C, op, event);
}
static int texture_paint_radial_control_exec(bContext *C, wmOperator *op)
{
Brush *brush = paint_brush(&CTX_data_scene(C)->toolsettings->imapaint.paint);
- int ret = brush_radial_control_exec(op, brush, 2);
+ int ret = brush_radial_control_exec(op, brush, 1);
char str[256];
WM_radial_control_string(op, str, 256);
@@ -5343,7 +5362,7 @@ void PAINT_OT_texture_paint_radial_control(wmOperatorType *ot)
ot->poll= texture_paint_poll;
/* flags */
- ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
}
@@ -5412,21 +5431,21 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
/* override */
ps.is_texbrush= 0;
ps.is_airbrush= 1;
- orig_brush_size= ps.brush->size;
- ps.brush->size= 32; /* cover the whole image */
+ orig_brush_size= brush_size(ps.brush);
+ brush_set_size(ps.brush, 32); /* cover the whole image */
ps.tool= PAINT_TOOL_DRAW; /* so pixels are initialized with minimal info */
scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING;
- undo_paint_push_begin(UNDO_PAINT_IMAGE, "Image Paint",
+ undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name,
image_undo_restore, image_undo_free);
/* allocate and initialize spacial data structures */
project_paint_begin(&ps);
if(ps.dm==NULL) {
- ps.brush->size= orig_brush_size;
+ brush_set_size(ps.brush, orig_brush_size);
return OPERATOR_CANCELLED;
}
else {
@@ -5450,7 +5469,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
project_paint_end(&ps);
scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
- ps.brush->size= orig_brush_size;
+ brush_set_size(ps.brush, orig_brush_size);
return OPERATOR_FINISHED;
}
@@ -5488,14 +5507,14 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op)
int h= settings->imapaint.screen_grab_size[1];
int maxsize;
- RNA_string_get(op->ptr, "filename", filename);
+ RNA_string_get(op->ptr, "filepath", filename);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxsize);
if(w > maxsize) w= maxsize;
if(h > maxsize) h= maxsize;
- ibuf= ED_view3d_draw_offscreen_imbuf(CTX_data_scene(C), CTX_wm_view3d(C), CTX_wm_region(C), w, h);
+ ibuf= ED_view3d_draw_offscreen_imbuf(CTX_data_scene(C), CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect);
image= BKE_add_image_imbuf(ibuf);
if(image) {
@@ -5541,5 +5560,5 @@ void PAINT_OT_image_from_view(wmOperatorType *ot)
/* flags */
ot->flag= OPTYPE_REGISTER;
- RNA_def_string_file_name(ot->srna, "filename", "", FILE_MAX, "File Name", "Name of the file");
+ RNA_def_string_file_name(ot->srna, "filepath", "", FILE_MAX, "File Path", "Name of the file");
}
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index b25eec70311..3ed314095ef 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -110,11 +110,18 @@ void PAINT_OT_face_select_all(struct wmOperatorType *ot);
int facemask_paint_poll(struct bContext *C);
+/* stroke operator */
+typedef enum wmBrushStrokeMode {
+ WM_BRUSHSTROKE_NORMAL,
+ WM_BRUSHSTROKE_INVERT,
+ WM_BRUSHSTROKE_SMOOTH,
+} wmBrushStrokeMode;
+
/* paint_undo.c */
typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb);
typedef void (*UndoFreeCb)(struct ListBase *lb);
-void undo_paint_push_begin(int type, char *name, UndoRestoreCb restore, UndoFreeCb free);
+void undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free);
struct ListBase *undo_paint_push_get_list(int type);
void undo_paint_push_count_alloc(int type, int size);
void undo_paint_push_end(int type);
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index abd56babd8d..b5cadb9c484 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -39,28 +39,25 @@
#include "sculpt_intern.h"
#include <string.h>
+//#include <stdio.h>
/* Brush operators */
static int brush_add_exec(bContext *C, wmOperator *op)
{
/*int type = RNA_enum_get(op->ptr, "type");*/
- Brush *br = NULL;
+ Paint *paint = paint_get_active(CTX_data_scene(C));
+ struct Brush *br = paint_brush(paint);
- br = add_brush("Brush");
+ if (br)
+ br = copy_brush(br);
+ else
+ br = add_brush("Brush");
- if(br)
- paint_brush_set(paint_get_active(CTX_data_scene(C)), br);
+ paint_brush_set(paint_get_active(CTX_data_scene(C)), br);
return OPERATOR_FINISHED;
}
-static EnumPropertyItem brush_type_items[] = {
- {OB_MODE_SCULPT, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt", ""},
- {OB_MODE_VERTEX_PAINT, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""},
- {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
- {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
- {0, NULL, 0, NULL, NULL}};
-
void BRUSH_OT_add(wmOperatorType *ot)
{
/* identifiers */
@@ -73,8 +70,63 @@ void BRUSH_OT_add(wmOperatorType *ot)
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+
+static int brush_scale_size_exec(bContext *C, wmOperator *op)
+{
+ Paint *paint= paint_get_active(CTX_data_scene(C));
+ struct Brush *brush= paint_brush(paint);
+ // Object *ob= CTX_data_active_object(C);
+ float scalar= RNA_float_get(op->ptr, "scalar");
+
+ if (brush) {
+ // pixel radius
+ {
+ const int old_size= brush_size(brush);
+ int size= (int)(scalar*old_size);
+
+ if (old_size == size) {
+ if (scalar > 1) {
+ size++;
+ }
+ else if (scalar < 1) {
+ size--;
+ }
+ }
+ CLAMP(size, 1, 2000); // XXX magic number
+
+ brush_set_size(brush, size);
+ }
+
+ // unprojected radius
+ {
+ float unprojected_radius= scalar*brush_unprojected_radius(brush);
+
+ if (unprojected_radius < 0.001) // XXX magic number
+ unprojected_radius= 0.001f;
+
+ brush_set_unprojected_radius(brush, unprojected_radius);
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void BRUSH_OT_scale_size(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Scale Sculpt/Paint Brush Size";
+ ot->description= "Change brush size by a scalar";
+ ot->idname= "BRUSH_OT_scale_size";
+
+ /* api callbacks */
+ ot->exec= brush_scale_size_exec;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
- RNA_def_enum(ot->srna, "type", brush_type_items, OB_MODE_VERTEX_PAINT, "Type", "Which paint mode to create the brush for.");
+ RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2);
}
static int vertex_color_set_exec(bContext *C, wmOperator *op)
@@ -102,13 +154,44 @@ void PAINT_OT_vertex_color_set(wmOperatorType *ot)
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
+static int brush_reset_exec(bContext *C, wmOperator *op)
+{
+ Paint *paint = paint_get_active(CTX_data_scene(C));
+ struct Brush *brush = paint_brush(paint);
+ Object *ob = CTX_data_active_object(C);
+
+ if(!ob) return OPERATOR_CANCELLED;
+
+ if(ob->mode & OB_MODE_SCULPT)
+ brush_reset_sculpt(brush);
+ /* TODO: other modes */
+
+ return OPERATOR_FINISHED;
+}
+
+void BRUSH_OT_reset(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Reset Brush";
+ ot->description= "Return brush to defaults based on current tool";
+ ot->idname= "BRUSH_OT_reset";
+
+ /* api callbacks */
+ ot->exec= brush_reset_exec;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
/**************************** registration **********************************/
void ED_operatortypes_paint(void)
{
/* brush */
WM_operatortype_append(BRUSH_OT_add);
+ WM_operatortype_append(BRUSH_OT_scale_size);
WM_operatortype_append(BRUSH_OT_curve_preset);
+ WM_operatortype_append(BRUSH_OT_reset);
/* image */
WM_operatortype_append(PAINT_OT_texture_paint_toggle);
@@ -121,7 +204,6 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_project_image);
WM_operatortype_append(PAINT_OT_image_from_view);
-
/* weight */
WM_operatortype_append(PAINT_OT_weight_paint_toggle);
WM_operatortype_append(PAINT_OT_weight_paint_radial_control);
@@ -141,54 +223,83 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_face_select_all);
}
+
static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path)
{
wmKeyMapItem *kmi;
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ONEKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 0);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", TWOKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 1);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", THREEKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 2);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FOURKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 3);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FIVEKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 4);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SIXKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 5);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SEVENKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 6);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", EIGHTKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 7);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", NINEKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 8);
kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ZEROKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
+ RNA_string_set(kmi->ptr, "data_path", path);
RNA_int_set(kmi->ptr, "value", 9);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ONEKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 10);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", TWOKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 11);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", THREEKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 12);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FOURKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 13);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", FIVEKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 14);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SIXKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 15);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", SEVENKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 16);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", EIGHTKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 17);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", NINEKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 18);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_set_int", ZEROKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", path);
+ RNA_int_set(kmi->ptr, "value", 19);
}
static void ed_keymap_paint_brush_size(wmKeyMap *keymap, const char *path)
{
wmKeyMapItem *kmi;
-
- kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", LEFTBRACKETKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
- RNA_float_set(kmi->ptr, "value", 0.9);
-
- kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", RIGHTBRACKETKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", path);
- RNA_float_set(kmi->ptr, "value", 10.0/9.0); // 1.1111....
-}
+
+ kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", LEFTBRACKETKEY, KM_PRESS, 0, 0);
+ RNA_float_set(kmi->ptr, "scalar", 0.9);
+
+ kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", RIGHTBRACKETKEY, KM_PRESS, 0, 0);
+ RNA_float_set(kmi->ptr, "scalar", 10.0/9.0); // 1.1111....
+}
void ED_keymap_paint(wmKeyConfig *keyconf)
{
@@ -200,12 +311,15 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
keymap= WM_keymap_find(keyconf, "Sculpt", 0, 0);
keymap->poll= sculpt_poll;
- RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE);
RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
- RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE);
- WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0);
- WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "mode", WM_BRUSHSTROKE_NORMAL);
+ RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_BRUSHSTROKE_INVERT);
+ RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_BRUSHSTROKE_SMOOTH);
+
+ //stroke_mode_modal_keymap(keyconf);
for(i=0; i<=5; i++)
RNA_int_set(WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY+i, KM_PRESS, KM_CTRL, 0)->ptr, "level", i);
@@ -221,44 +335,56 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_switch(keymap, "tool_settings.sculpt.active_brush_index");
ed_keymap_paint_brush_size(keymap, "tool_settings.sculpt.brush.size");
-
+
+ /* */
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", AKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.brush.use_anchor");
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_anchor");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", SKEY, KM_PRESS, KM_SHIFT, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.brush.use_smooth_stroke");
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_smooth_stroke");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", RKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.brush.use_rake");
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_rake");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", AKEY, KM_PRESS, KM_SHIFT, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.brush.use_airbrush");
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_airbrush");
/* brush switching */
- kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", DKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name");
- RNA_string_set(kmi->ptr, "value", "Draw");
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", DKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
+ RNA_string_set(kmi->ptr, "value", "SculptDraw");
- kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", SKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name");
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", SKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
RNA_string_set(kmi->ptr, "value", "Smooth");
- kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", PKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name");
- RNA_string_set(kmi->ptr, "value", "Pinch");
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", PKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
+ RNA_string_set(kmi->ptr, "value", "Pinch/Magnify");
- kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", GKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name");
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", GKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
RNA_string_set(kmi->ptr, "value", "Grab");
- kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", LKEY, KM_PRESS, 0, 0);
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name");
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", LKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
RNA_string_set(kmi->ptr, "value", "Layer");
+
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", CKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
+ RNA_string_set(kmi->ptr, "value", "Crease");
+
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", CKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
+ RNA_string_set(kmi->ptr, "value", "Clay");
- kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", TKEY, KM_PRESS, KM_SHIFT, 0); // was just T in 2.4x
- RNA_string_set(kmi->ptr, "path", "tool_settings.sculpt.active_brush_name");
- RNA_string_set(kmi->ptr, "value", "Flatten");
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", IKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
+ RNA_string_set(kmi->ptr, "value", "Inflate/Deflate");
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_id", TKEY, KM_PRESS, KM_SHIFT, 0); // was just T in 2.4x
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush");
+ RNA_string_set(kmi->ptr, "value", "Flatten/Contrast");
/* Vertex Paint mode */
keymap= WM_keymap_find(keyconf, "Vertex Paint", 0, 0);
@@ -276,7 +402,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_size(keymap, "tool_settings.vertex_paint.brush.size");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */
- RNA_string_set(kmi->ptr, "path", "vertex_paint_object.data.use_paint_mask");
+ RNA_string_set(kmi->ptr, "data_path", "vertex_paint_object.data.use_paint_mask");
/* Weight Paint mode */
keymap= WM_keymap_find(keyconf, "Weight Paint", 0, 0);
@@ -294,7 +420,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_size(keymap, "tool_settings.weight_paint.brush.size");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */
- RNA_string_set(kmi->ptr, "path", "weight_paint_object.data.use_paint_mask");
+ RNA_string_set(kmi->ptr, "data_path", "weight_paint_object.data.use_paint_mask");
WM_keymap_verify_item(keymap, "PAINT_OT_weight_from_bones", WKEY, KM_PRESS, 0, 0);
@@ -314,7 +440,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
ed_keymap_paint_brush_size(keymap, "tool_settings.image_paint.brush.size");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */
- RNA_string_set(kmi->ptr, "path", "texture_paint_object.data.use_paint_mask");
+ RNA_string_set(kmi->ptr, "data_path", "texture_paint_object.data.use_paint_mask");
/* face-mask mode */
keymap= WM_keymap_find(keyconf, "Face Mask", 0, 0);
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index c25b5685844..6d58731d79c 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -20,7 +20,7 @@
*
* The Original Code is: all of this file.
*
- * Contributor(s): none yet.
+ * Contributor(s): Jason Wilkins, Tom Musgrove.
*
* ***** END GPL LICENSE BLOCK *****
*
@@ -30,11 +30,13 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_brush_types.h"
#include "RNA_access.h"
#include "BKE_context.h"
#include "BKE_paint.h"
+#include "BKE_brush.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -49,6 +51,8 @@
#include "ED_view3d.h"
#include "paint_intern.h"
+#include "sculpt_intern.h" // XXX, for expedience in getting this working, refactor later (or this just shows that this needs unification)
+
#include <float.h>
#include <math.h>
@@ -65,7 +69,7 @@ typedef struct PaintStroke {
float last_mouse_position[2];
- /* Set whether any stroke step has yet occured
+ /* Set whether any stroke step has yet occurred
e.g. in sculpt mode, stroke doesn't start until cursor
passes over the mesh */
int stroke_started;
@@ -96,54 +100,751 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata
glDisable(GL_LINE_SMOOTH);
}
-static void paint_draw_cursor(bContext *C, int x, int y, void *customdata)
+#if 0
+
+// grid texture for testing
+
+#define GRID_WIDTH 8
+#define GRID_LENGTH 8
+
+#define W (0xFFFFFFFF)
+#define G (0x00888888)
+#define E (0xE1E1E1E1)
+#define C (0xC3C3C3C3)
+#define O (0xB4B4B4B4)
+#define Q (0xA9A9A9A9)
+
+static unsigned grid_texture0[256] =
+{
+ W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+ W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,
+};
+
+static unsigned grid_texture1[64] =
{
- Paint *paint = paint_get_active(CTX_data_scene(C));
- Brush *brush = paint_brush(paint);
+ C,C,C,C,C,C,C,C,
+ C,G,G,G,G,G,G,C,
+ C,G,G,G,G,G,G,C,
+ C,G,G,G,G,G,G,C,
+ C,G,G,G,G,G,G,C,
+ C,G,G,G,G,G,G,C,
+ C,G,G,G,G,G,G,C,
+ C,C,C,C,C,C,C,C,
+};
+
+static unsigned grid_texture2[16] =
+{
+ O,O,O,O,
+ O,G,G,O,
+ O,G,G,O,
+ O,O,O,O,
+};
- if(!(paint->flags & PAINT_SHOW_BRUSH))
- return;
+static unsigned grid_texture3[4] =
+{
+ Q,Q,
+ Q,Q,
+};
- glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col);
- glEnable(GL_LINE_SMOOTH);
- glEnable(GL_BLEND);
+static unsigned grid_texture4[1] =
+{
+ Q,
+};
- glTranslatef((float)x, (float)y, 0.0f);
- glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40);
- glTranslatef((float)-x, (float)-y, 0.0f);
+#undef W
+#undef G
+#undef E
+#undef C
+#undef O
+#undef Q
- glDisable(GL_BLEND);
- glDisable(GL_LINE_SMOOTH);
+static void load_grid()
+{
+ static GLuint overlay_texture;
+
+ if (!overlay_texture) {
+ //GLfloat largest_supported_anisotropy;
+
+ glGenTextures(1, &overlay_texture);
+ glBindTexture(GL_TEXTURE_2D, overlay_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture0);
+ glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture1);
+ glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture2);
+ glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture3);
+ glTexImage2D(GL_TEXTURE_2D, 4, GL_RGB, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture4);
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+
+ //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ //glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy);
+ //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy);
+ }
+}
+
+#endif
+
+extern float get_tex_pixel(Brush* br, float u, float v);
+
+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 &&
+ mtex->ofs[0] == snap->ofs[0] &&
+ mtex->ofs[1] == snap->ofs[1] &&
+ mtex->ofs[2] == snap->ofs[2] &&
+ mtex->size[0] == snap->size[0] &&
+ mtex->size[1] == snap->size[1] &&
+ mtex->size[2] == snap->size[2] &&
+ mtex->rot == snap->rot) &&
+ ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && brush_size(brush) <= snap->brush_size) || (brush_size(brush) == snap->brush_size)) && // make brush smaller shouldn't cause a resample
+ 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(brush);
+ snap->winx = vc->ar->winx;
+ snap->winy = vc->ar->winy;
+}
+
+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 = 0;
+
+ int size;
+ int j;
+ int refresh;
+
+ 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(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(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.001 || rotation < -0.001)) {
+ const float angle = atan2(y, x) + rotation;
+
+ x = len * cos(angle);
+ y = len * sin(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 ? 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);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ }
+
+ return 1;
+}
+
+/* Convert a point in model coordinates to 2D screen coordinates. */
+// XXX duplicated from sculpt.c, deal with this later.
+static void projectf(bglMats *mats, const float v[3], float p[2])
+{
+ double ux, uy, uz;
+
+ gluProject(v[0],v[1],v[2], mats->modelview, mats->projection,
+ (GLint *)mats->viewport, &ux, &uy, &uz);
+ p[0]= ux;
+ p[1]= uy;
+}
+
+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];
+
+ viewvector(rv3d, location, view);
+
+ // create a vector that is not orthogonal to view
+
+ if (fabsf(view[0]) < 0.1) {
+ nonortho[0] = view[0] + 1;
+ nonortho[1] = view[1];
+ nonortho[2] = view[2];
+ }
+ else if (fabsf(view[1]) < 0.1) {
+ nonortho[0] = view[0];
+ nonortho[1] = view[1] + 1;
+ nonortho[2] = view[2];
+ }
+ else {
+ nonortho[0] = view[0];
+ nonortho[1] = view[1];
+ nonortho[2] = view[2] + 1;
+ }
+
+ // 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);
+}
+
+int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, float location[3], float modelview[16], float projection[16], int viewport[4])
+{
+ struct PaintStroke *stroke;
+ float window[2];
+ int hit;
+
+ stroke = paint_stroke_new(C, NULL, NULL, NULL, NULL);
+
+ window[0] = x + stroke->vc.ar->winrct.xmin;
+ window[1] = y + stroke->vc.ar->winrct.ymin;
+
+ memcpy(modelview, stroke->vc.rv3d->viewmat, sizeof(float[16]));
+ memcpy(projection, stroke->vc.rv3d->winmat, sizeof(float[16]));
+ memcpy(viewport, stroke->mats.viewport, sizeof(int[4]));
+
+ 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(stroke->brush), location, &stroke->mats);
+
+ if (*pixel_radius == 0)
+ *pixel_radius = brush_size(stroke->brush);
+
+ mul_m4_v3(stroke->vc.obact->sculpt->ob->obmat, location);
+
+ hit = 1;
+ }
+ else {
+ Sculpt* sd = CTX_data_tool_settings(C)->sculpt;
+ Brush* brush = paint_brush(&sd->paint);
+
+ *pixel_radius = brush_size(brush);
+ hit = 0;
+ }
+
+ paint_stroke_free(stroke);
+
+ return hit;
+}
+
+// XXX duplicated from sculpt.c
+float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset)
+{
+ float delta[3], scale, loc[3];
+
+ mul_v3_m4v3(loc, ob->obmat, center);
+
+ initgrabz(vc->rv3d, loc[0], loc[1], loc[2]);
+ window_to_3d_delta(vc->ar, delta, offset, 0);
+
+ scale= fabsf(mat4_to_scale(ob->obmat));
+ scale= (scale == 0.0f)? 1.0f: scale;
+
+ return len_v3(delta)/scale;
+}
+
+// XXX paint cursor now does a lot of the same work that is needed during a sculpt stroke
+// problem: all this stuff was not intended to be used at this point, so things feel a
+// bit hacked. I've put lots of stuff in Brush that probably better goes in Paint
+// Functions should be refactored so that they can be used between sculpt.c and
+// paint_stroke.c clearly and optimally and the lines of communication between the
+// two modules should be more clearly defined.
+static void paint_draw_cursor(bContext *C, int x, int y, void *unused)
+{
+ ViewContext vc;
+
+ (void)unused;
+
+ view3d_set_viewcontext(C, &vc);
+
+ if (vc.obact->sculpt) {
+ Paint *paint = paint_get_active(CTX_data_scene(C));
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Brush *brush = paint_brush(paint);
+
+ int pixel_radius, viewport[4];
+ float location[3], modelview[16], projection[16];
+
+ int hit;
+
+ int flip;
+ int sign;
+
+ float* col;
+ float alpha;
+
+ const float root_alpha = brush_alpha(brush);
+ float visual_strength = root_alpha*root_alpha;
+
+ const float min_alpha = 0.20f;
+ const float max_alpha = 0.80f;
+
+ {
+ 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;
+ }
+ }
+
+ if(!brush_use_locked_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH))
+ return;
+
+ hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport);
+
+ if (brush_use_locked_size(brush))
+ brush_set_size(brush, pixel_radius);
+
+ // XXX: no way currently to know state of pen flip or invert key modifier without starting a stroke
+ flip = 1;
+
+ sign = flip * ((brush->flag & BRUSH_DIR_IN)? -1 : 1);
+
+ if (sign < 0 && ELEM4(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH))
+ col = brush->sub_col;
+ else
+ col = brush->add_col;
+
+ alpha = (paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) ? min_alpha + (visual_strength*(max_alpha-min_alpha)) : 0.50f;
+
+ if (ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED) && brush->flag & BRUSH_TEXTURE_OVERLAY) {
+ 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) {
+ glTranslatef(0.5f, 0.5f, 0);
+
+ if (brush->flag & BRUSH_RAKE) {
+ glRotatef(sd->last_angle*(float)(180.0/M_PI), 0, 0, 1);
+ }
+ else {
+ glRotatef(sd->special_rotation*(float)(180.0/M_PI), 0, 0, 1);
+ }
+
+ glTranslatef(-0.5f, -0.5f, 0);
+
+ if (sd->draw_pressure && brush_use_size_pressure(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);
+ }
+ }
+
+ 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);
+
+ glBegin(GL_QUADS);
+ if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) {
+ if (sd->draw_anchored) {
+ glTexCoord2f(0, 0);
+ glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin);
+
+ glTexCoord2f(1, 0);
+ glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin);
+
+ glTexCoord2f(1, 1);
+ glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin);
+
+ glTexCoord2f(0, 1);
+ glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin);
+ }
+ else {
+ const int radius= brush_size(brush);
+
+ glTexCoord2f(0, 0);
+ glVertex2f((float)x-radius, (float)y-radius);
+
+ glTexCoord2f(1, 0);
+ glVertex2f((float)x+radius, (float)y-radius);
+
+ glTexCoord2f(1, 1);
+ glVertex2f((float)x+radius, (float)y+radius);
+
+ glTexCoord2f(0, 1);
+ glVertex2f((float)x-radius, (float)y+radius);
+ }
+ }
+ else {
+ glTexCoord2f(0, 0);
+ glVertex2f(0, 0);
+
+ glTexCoord2f(1, 0);
+ glVertex2f(viewport[2], 0);
+
+ glTexCoord2f(1, 1);
+ glVertex2f(viewport[2], viewport[3]);
+
+ glTexCoord2f(0, 1);
+ glVertex2f(0, viewport[3]);
+ }
+ glEnd();
+
+ glPopMatrix();
+ }
+
+ glPopAttrib();
+ }
+
+ if (hit) {
+ float unprojected_radius;
+
+ // XXX duplicated from brush_strength & paint_stroke_add_step, refactor later
+ //wmEvent* event = CTX_wm_window(C)->eventstate;
+
+ if (sd->draw_pressure && brush_use_alpha_pressure(brush))
+ visual_strength *= sd->pressure_value;
+
+ // don't show effect of strength past the soft limit
+ if (visual_strength > 1) visual_strength = 1;
+
+ if (sd->draw_anchored) {
+ unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, sd->anchored_size);
+ }
+ else {
+ if (brush->flag & BRUSH_ANCHORED)
+ unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, 8);
+ else
+ unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, brush_size(brush));
+ }
+
+ if (sd->draw_pressure && brush_use_size_pressure(brush))
+ unprojected_radius *= sd->pressure_value;
+
+ if (!brush_use_locked_size(brush))
+ brush_set_unprojected_radius(brush, unprojected_radius);
+
+ if(!(paint->flags & PAINT_SHOW_BRUSH))
+ return;
+
+ }
+
+ 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);
+
+ glColor4f(col[0], col[1], col[2], alpha);
+
+ glEnable(GL_BLEND);
+
+ glEnable(GL_LINE_SMOOTH);
+
+ if (sd->draw_anchored) {
+ glTranslatef(sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin, 0.0f);
+ glutil_draw_lined_arc(0.0, M_PI*2.0, sd->anchored_size, 40);
+ glTranslatef(-sd->anchored_initial_mouse[0] + vc.ar->winrct.xmin, -sd->anchored_initial_mouse[1] + vc.ar->winrct.xmin, 0.0f);
+ }
+ else {
+ glTranslatef((float)x, (float)y, 0.0f);
+ glutil_draw_lined_arc(0.0, M_PI*2.0, brush_size(brush), 40);
+ glTranslatef(-(float)x, -(float)y, 0.0f);
+ }
+
+ glPopAttrib();
+ }
+ else {
+ Paint *paint = paint_get_active(CTX_data_scene(C));
+ Brush *brush = paint_brush(paint);
+
+ if(!(paint->flags & PAINT_SHOW_BRUSH))
+ return;
+
+ glColor4f(brush->add_col[0], brush->add_col[1], brush->add_col[2], 0.5f);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+
+ glTranslatef((float)x, (float)y, 0.0f);
+ glutil_draw_lined_arc(0.0, M_PI*2.0, brush_size(brush), 40); // XXX: for now use the brushes size instead of potentially using the unified size because the feature has been enabled for sculpt
+ glTranslatef((float)-x, (float)-y, 0.0f);
+
+ glDisable(GL_BLEND);
+ glDisable(GL_LINE_SMOOTH);
+ }
}
/* 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, float mouse[2])
+static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2])
{
+ Paint *paint = paint_get_active(CTX_data_scene(C)); // XXX
+ Brush *brush = paint_brush(paint); // XXX
+
+ float mouse[3];
+
PointerRNA itemptr;
- float pressure = 1;
- float center[3] = {0, 0, 0};
- int flip= event->shift?1:0;
+
+ float location[3];
+
+ float pressure;
+ int pen_flip;
+
+ ViewContext vc; // XXX
+
PaintStroke *stroke = op->customdata;
- /* XXX: can remove the if statement once all modes have this */
- if(stroke->get_location)
- stroke->get_location(C, stroke, center, mouse);
+ view3d_set_viewcontext(C, &vc); // XXX
/* Tablet */
if(event->custom == EVT_DATA_TABLET) {
wmTabletData *wmtab= event->customdata;
- if(wmtab->Active != EVT_TABLET_NONE)
- pressure= wmtab->Pressure;
- if(wmtab->Active == EVT_TABLET_ERASER)
- flip = 1;
+
+ pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1;
+ pen_flip = (wmtab->Active == EVT_TABLET_ERASER);
}
-
+ else {
+ pressure = 1;
+ pen_flip = 0;
+ }
+
+ // XXX: temporary check for sculpt mode until things are more unified
+ if (vc.obact->sculpt) {
+ float delta[3];
+
+ brush_jitter_pos(brush, mouse_in, mouse);
+
+ // XXX: meh, this is round about because brush_jitter_pos isn't written in the best way to be reused here
+ if (brush->flag & BRUSH_JITTER_PRESSURE) {
+ sub_v3_v3v3(delta, mouse, mouse_in);
+ mul_v3_fl(delta, pressure);
+ add_v3_v3v3(mouse, mouse_in, delta);
+ }
+ }
+ else
+ copy_v3_v3(mouse, mouse_in);
+
+ /* XXX: can remove the if statement once all modes have this */
+ if(stroke->get_location)
+ stroke->get_location(C, stroke, location, mouse);
+ else
+ zero_v3(location);
+
/* Add to stroke */
RNA_collection_add(op->ptr, "stroke", &itemptr);
- RNA_float_set_array(&itemptr, "location", center);
- RNA_float_set_array(&itemptr, "mouse", mouse);
- RNA_boolean_set(&itemptr, "flip", flip);
- RNA_float_set(&itemptr, "pressure", pressure);
+
+ RNA_float_set_array(&itemptr, "location", location);
+ RNA_float_set_array(&itemptr, "mouse", mouse);
+ RNA_boolean_set (&itemptr, "pen_flip", pen_flip);
+ RNA_float_set (&itemptr, "pressure", pressure);
stroke->last_mouse_position[0] = mouse[0];
stroke->last_mouse_position[1] = mouse[1];
@@ -154,10 +855,14 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev
/* Returns zero if no sculpt changes should be made, non-zero otherwise */
static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event)
{
- output[0] = event->x;
+ output[0] = event->x;
output[1] = event->y;
- if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
+ if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) &&
+ !ELEM4(stroke->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK) &&
+ !(stroke->brush->flag & BRUSH_ANCHORED) &&
+ !(stroke->brush->flag & BRUSH_RESTORE_MESH))
+ {
float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u;
float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y;
@@ -176,7 +881,9 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *ev
/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
static int paint_space_stroke_enabled(Brush *br)
{
- return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
+ return (br->flag & BRUSH_SPACE) &&
+ !(br->flag & BRUSH_ANCHORED) &&
+ !ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK);
}
/* For brushes with stroke spacing enabled, moves mouse in steps
@@ -187,23 +894,34 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const
int cnt = 0;
if(paint_space_stroke_enabled(stroke->brush)) {
- float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]};
- float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]};
+ float mouse[2];
+ float vec[2];
float length, scale;
- int steps = 0, i;
- /* Normalize the vector between the last stroke dot and the goal */
- length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
+ copy_v2_v2(mouse, stroke->last_mouse_position);
+ sub_v2_v2v2(vec, final_mouse, mouse);
+
+ length = len_v2(vec);
if(length > FLT_EPSILON) {
- scale = stroke->brush->spacing / length;
- vec[0] *= scale;
- vec[1] *= scale;
+ int steps;
+ int i;
+ float pressure = 1;
+
+ // XXX duplicate code
+ if(event->custom == EVT_DATA_TABLET) {
+ wmTabletData *wmtab= event->customdata;
+ if(wmtab->Active != EVT_TABLET_NONE)
+ pressure = brush_use_size_pressure(stroke->brush) ? wmtab->Pressure : 1;
+ }
+
+ scale = (brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length;
+ mul_v2_fl(vec, scale);
+
+ steps = (int)(1.0f / scale);
- steps = (int)(length / stroke->brush->spacing);
for(i = 0; i < steps; ++i, ++cnt) {
- mouse[0] += vec[0];
- mouse[1] += vec[1];
+ add_v2_v2(mouse, vec);
paint_brush_stroke_add_step(C, op, event, mouse);
}
}
@@ -275,21 +993,34 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
MEM_freeN(stroke);
return OPERATOR_FINISHED;
}
- else if(first || event->type == MOUSEMOVE || (event->type == TIMER && (event->customdata == stroke->timer))) {
+ else if(first || ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (event->type == TIMER && (event->customdata == stroke->timer))) {
if(stroke->stroke_started) {
if(paint_smooth_stroke(stroke, mouse, event)) {
if(paint_space_stroke_enabled(stroke->brush)) {
- if(!paint_space_stroke(C, op, event, mouse))
- ;//ED_region_tag_redraw(ar);
+ if(!paint_space_stroke(C, op, event, mouse)) {
+ //ED_region_tag_redraw(ar);
+ }
}
- else
+ else {
paint_brush_stroke_add_step(C, op, event, mouse);
+ }
}
- else
+ else {
;//ED_region_tag_redraw(ar);
+ }
}
}
+ /* we want the stroke to have the first daub at the start location instead of waiting till we have moved the space distance */
+ if(first &&
+ stroke->stroke_started &&
+ paint_space_stroke_enabled(stroke->brush) &&
+ !(stroke->brush->flag & BRUSH_ANCHORED) &&
+ !(stroke->brush->flag & BRUSH_SMOOTH_STROKE))
+ {
+ paint_brush_stroke_add_step(C, op, event, mouse);
+ }
+
return OPERATOR_RUNNING_MODAL;
}
diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c
index 5da988f1ae1..c2f82b8e2e0 100644
--- a/source/blender/editors/sculpt_paint/paint_undo.c
+++ b/source/blender/editors/sculpt_paint/paint_undo.c
@@ -76,7 +76,7 @@ static void undo_elem_free(UndoStack *stack, UndoElem *uel)
}
}
-static void undo_stack_push_begin(UndoStack *stack, char *name, UndoRestoreCb restore, UndoFreeCb free)
+static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free)
{
UndoElem *uel;
int nr;
@@ -145,27 +145,35 @@ static void undo_stack_push_end(UndoStack *stack)
}
}
-static void undo_stack_step(bContext *C, UndoStack *stack, int step)
+static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name)
{
UndoElem *undo;
if(step==1) {
if(stack->current==NULL);
else {
- if(G.f & G_DEBUG) printf("undo %s\n", stack->current->name);
- undo_restore(C, stack, stack->current);
- stack->current= stack->current->prev;
+ if(!name || strcmp(stack->current->name, name) == 0) {
+ if(G.f & G_DEBUG) printf("undo %s\n", stack->current->name);
+ undo_restore(C, stack, stack->current);
+ stack->current= stack->current->prev;
+ return 1;
+ }
}
}
else if(step==-1) {
if((stack->current!=NULL && stack->current->next==NULL) || stack->elems.first==NULL);
else {
- undo= (stack->current && stack->current->next)? stack->current->next: stack->elems.first;
- undo_restore(C, stack, undo);
- stack->current= undo;
- if(G.f & G_DEBUG) printf("redo %s\n", undo->name);
+ if(!name || strcmp(stack->current->name, name) == 0) {
+ undo= (stack->current && stack->current->next)? stack->current->next: stack->elems.first;
+ undo_restore(C, stack, undo);
+ stack->current= undo;
+ if(G.f & G_DEBUG) printf("redo %s\n", undo->name);
+ return 1;
+ }
}
}
+
+ return 0;
}
static void undo_stack_free(UndoStack *stack)
@@ -181,7 +189,7 @@ static void undo_stack_free(UndoStack *stack)
/* Exported Functions */
-void undo_paint_push_begin(int type, char *name, UndoRestoreCb restore, UndoFreeCb free)
+void undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free)
{
if(type == UNDO_PAINT_IMAGE)
undo_stack_push_begin(&ImageUndoStack, name, restore, free);
@@ -219,12 +227,14 @@ void undo_paint_push_end(int type)
undo_stack_push_end(&MeshUndoStack);
}
-void ED_undo_paint_step(bContext *C, int type, int step)
+int ED_undo_paint_step(bContext *C, int type, int step, const char *name)
{
if(type == UNDO_PAINT_IMAGE)
- undo_stack_step(C, &ImageUndoStack, step);
+ return undo_stack_step(C, &ImageUndoStack, step, name);
else if(type == UNDO_PAINT_MESH)
- undo_stack_step(C, &MeshUndoStack, step);
+ return undo_stack_step(C, &MeshUndoStack, step, name);
+
+ return 0;
}
void ED_undo_paint_free(void)
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index 85fbd5954e8..0098b8ca12c 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -7,6 +7,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_brush_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -14,13 +15,10 @@
#include "BLI_math.h"
#include "BKE_brush.h"
-#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_DerivedMesh.h"
-#include "BKE_global.h"
#include "BKE_paint.h"
-#include "BKE_utildefines.h"
#include "BIF_gl.h"
@@ -216,6 +214,9 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot)
{CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""},
{CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""},
{CURVE_PRESET_MAX, "MAX", 0, "Max", ""},
+ {CURVE_PRESET_LINE, "LINE", 0, "Line", ""},
+ {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""},
+ {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""},
{0, NULL, 0, NULL, NULL}};
ot->name= "Preset";
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 7bfb7716c9c..399ba535e57 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -38,42 +38,38 @@
#include "MEM_guardedalloc.h"
-#include "IMB_imbuf.h"
-#include "IMB_imbuf_types.h"
-
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_ghash.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
#include "DNA_armature_types.h"
#include "DNA_mesh_types.h"
#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_brush_types.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
-#include "BKE_armature.h"
+#include "BKE_DerivedMesh.h"
#include "BKE_action.h"
#include "BKE_brush.h"
-#include "BKE_DerivedMesh.h"
-#include "BKE_cloth.h"
#include "BKE_context.h"
-#include "BKE_customdata.h"
#include "BKE_depsgraph.h"
#include "BKE_deform.h"
-#include "BKE_displist.h"
-#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
-#include "BKE_utildefines.h"
#include "WM_api.h"
#include "WM_types.h"
-#include "BIF_gl.h"
-#include "BIF_glutil.h"
#include "ED_armature.h"
#include "ED_mesh.h"
@@ -99,7 +95,7 @@ int vertex_paint_mode_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- return ob && ob->mode == OB_MODE_VERTEX_PAINT;
+ return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totface;
}
int vertex_paint_poll(bContext *C)
@@ -111,8 +107,8 @@ int vertex_paint_poll(bContext *C)
ARegion *ar= CTX_wm_region(C);
if(ar->regiontype==RGN_TYPE_WINDOW)
return 1;
+ }
}
- }
return 0;
}
@@ -120,7 +116,7 @@ int weight_paint_mode_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- return ob && ob->mode == OB_MODE_WEIGHT_PAINT;
+ return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totface;
}
int weight_paint_poll(bContext *C)
@@ -386,9 +382,8 @@ void wpaint_fill(VPaint *wp, Object *ob, float paintweight)
int actdef= 0;
char name[32];
- BLI_strncpy(name, defgroup->name, 32);
- bone_flip_name(name, 0); /* 0 = don't strip off number extensions */
-
+ flip_side_name(name, defgroup->name, FALSE);
+
for (curdef = ob->defbase.first; curdef; curdef=curdef->next, actdef++)
if (!strcmp(curdef->name, name))
break;
@@ -663,7 +658,7 @@ static void vpaint_blend(VPaint *vp, unsigned int *col, unsigned int *colorig, u
unsigned int testcol=0, a;
char *cp, *ct, *co;
- alpha= (int)(255.0*brush->alpha);
+ alpha= (int)(255.0*brush_alpha(brush));
if(brush->vertexpaint_tool==VP_MIX || brush->vertexpaint_tool==VP_BLUR) testcol= mcol_blend( *colorig, paintcol, alpha);
else if(brush->vertexpaint_tool==VP_ADD) testcol= mcol_add( *colorig, paintcol, alpha);
@@ -733,23 +728,24 @@ static float calc_vp_alpha_dl(VPaint *vp, ViewContext *vc, float vpimat[][3], fl
float fac, fac_2, size, dx, dy;
float alpha;
short vertco[2];
-
+ const int radius= brush_size(brush);
+
project_short_noclip(vc->ar, vert_nor, vertco);
dx= mval[0]-vertco[0];
dy= mval[1]-vertco[1];
- if (brush->flag & BRUSH_SIZE_PRESSURE)
- size = pressure * brush->size;
+ if (brush_use_size_pressure(brush))
+ size = pressure * radius;
else
- size = brush->size;
+ size = radius;
fac_2= dx*dx + dy*dy;
if(fac_2 > size*size) return 0.f;
fac = sqrtf(fac_2);
- alpha= brush->alpha * brush_curve_strength_clamp(brush, fac, size);
+ alpha= brush_alpha(brush) * brush_curve_strength_clamp(brush, fac, size);
- if (brush->flag & BRUSH_ALPHA_PRESSURE)
+ if (brush_use_alpha_pressure(brush))
alpha *= pressure;
if(vp->flag & VP_NORMALS) {
@@ -813,7 +809,7 @@ static void wpaint_blend(VPaint *wp, MDeformWeight *dw, MDeformWeight *uw, float
if((wp->flag & VP_SPRAY)==0) {
float testw=0.0f;
- alpha= brush->alpha;
+ alpha= brush_alpha(brush);
if(tool==VP_MIX || tool==VP_BLUR)
testw = paintval*alpha + uw->weight*(1.0-alpha);
else if(tool==VP_ADD)
@@ -864,6 +860,7 @@ void sample_wpaint(Scene *scene, ARegion *ar, View3D *v3d, int mode)
Mesh *me= get_mesh(ob);
int index;
short mval[2] = {0, 0}, sco[2];
+ int vgroup= ob->actdef-1;
if (!me) return;
@@ -938,7 +935,6 @@ void sample_wpaint(Scene *scene, ARegion *ar, View3D *v3d, int mode)
}
else {
DerivedMesh *dm;
- MDeformWeight *dw;
float w1, w2, w3, w4, co[3], fac;
dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
@@ -968,21 +964,17 @@ void sample_wpaint(Scene *scene, ARegion *ar, View3D *v3d, int mode)
fac= MIN4(w1, w2, w3, w4);
if(w1==fac) {
- dw= defvert_find_index(me->dvert+mface->v1, ob->actdef-1);
- if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f;
+ ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v1, vgroup);
}
else if(w2==fac) {
- dw= defvert_find_index(me->dvert+mface->v2, ob->actdef-1);
- if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f;
+ ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v2, vgroup);
}
else if(w3==fac) {
- dw= defvert_find_index(me->dvert+mface->v3, ob->actdef-1);
- if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f;
+ ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v3, vgroup);
}
else if(w4==fac) {
if(mface->v4) {
- dw= defvert_find_index(me->dvert+mface->v4, ob->actdef-1);
- if(dw) ts->vgroup_weight= dw->weight; else ts->vgroup_weight= 0.0f;
+ ts->vgroup_weight= defvert_find_weight(me->dvert+mface->v4, vgroup);
}
}
}
@@ -1123,8 +1115,6 @@ static int set_wpaint(bContext *C, wmOperator *op) /* toggle */
/* for switching to/from mode */
static int paint_poll_test(bContext *C)
{
- if(ED_operator_view3d_active(C)==0)
- return 0;
if(CTX_data_edit_object(C))
return 0;
if(CTX_data_active_object(C)==NULL)
@@ -1154,10 +1144,16 @@ static int vpaint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *ev
{
Paint *p = paint_get_active(CTX_data_scene(C));
Brush *brush = paint_brush(p);
+ float col[4];
WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor);
p->paint_cursor = NULL;
brush_radial_control_invoke(op, brush, 1);
+
+ copy_v3_v3(col, brush->add_col);
+ col[3]= 0.5f;
+ RNA_float_set_array(op->ptr, "color", col);
+
return WM_radial_control_invoke(C, op, event);
}
@@ -1183,10 +1179,16 @@ static int wpaint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *ev
{
Paint *p = paint_get_active(CTX_data_scene(C));
Brush *brush = paint_brush(p);
+ float col[4];
WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor);
p->paint_cursor = NULL;
brush_radial_control_invoke(op, brush, 1);
+
+ copy_v3_v3(col, brush->add_col);
+ col[3]= 0.5f;
+ RNA_float_set_array(op->ptr, "color", col);
+
return WM_radial_control_invoke(C, op, event);
}
@@ -1262,7 +1264,7 @@ static char *wpaint_make_validmap(Mesh *me, Object *ob)
bPose *pose;
bPoseChannel *chan;
ArmatureModifierData *amd;
- GHash *gh = BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp);
+ GHash *gh = BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp, "wpaint_make_validmap gh");
int i = 0, step1=1;
/*add all names to a hash table*/
@@ -1395,10 +1397,9 @@ static int wpaint_stroke_test_start(bContext *C, wmOperator *op, wmEvent *event)
bDeformGroup *curdef;
int actdef= 0;
char name[32];
-
- BLI_strncpy(name, defgroup->name, 32);
- bone_flip_name(name, 0); /* 0 = don't strip off number extensions */
-
+
+ flip_side_name(name, defgroup->name, FALSE);
+
for (curdef = ob->defbase.first; curdef; curdef=curdef->next, actdef++)
if (!strcmp(curdef->name, name))
break;
@@ -1450,7 +1451,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
/* load projection matrix */
mul_m4_m4m4(mat, ob->obmat, vc->rv3d->persmat);
- flip = RNA_boolean_get(itemptr, "flip");
+ flip = RNA_boolean_get(itemptr, "pen_flip");
pressure = RNA_float_get(itemptr, "pressure");
RNA_float_get_array(itemptr, "mouse", mval);
mval[0]-= vc->ar->winrct.xmin;
@@ -1460,7 +1461,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
/* which faces are involved */
if(wp->flag & VP_AREA) {
- totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush->size);
+ totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush_size(brush));
}
else {
indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]);
@@ -1746,7 +1747,6 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
-
}
@@ -1866,7 +1866,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
float pressure, mval[2];
RNA_float_get_array(itemptr, "mouse", mval);
- flip = RNA_boolean_get(itemptr, "flip");
+ flip = RNA_boolean_get(itemptr, "pen_flip");
pressure = RNA_float_get(itemptr, "pressure");
view3d_operator_needs_opengl(C);
@@ -1880,7 +1880,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
/* which faces are involved */
if(vp->flag & VP_AREA) {
- totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush->size);
+ totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush_size(brush));
}
else {
indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]);
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 509a1bcc61e..10af35a1b4b 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -22,7 +22,7 @@
*
* The Original Code is: all of this file.
*
- * Contributor(s): none yet.
+ * Contributor(s): Jason Wilkins, Tom Musgrove.
*
* ***** END GPL LICENSE BLOCK *****
*
@@ -38,32 +38,25 @@
#include "BLI_ghash.h"
#include "BLI_pbvh.h"
#include "BLI_threads.h"
+#include "BLI_editVert.h"
+#include "BLI_rand.h"
-#include "DNA_key_types.h"
-#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_brush_types.h"
#include "BKE_brush.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_context.h"
-#include "BKE_customdata.h"
-#include "BKE_DerivedMesh.h"
#include "BKE_depsgraph.h"
-#include "BKE_global.h"
-#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_library.h"
-#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_paint.h"
#include "BKE_report.h"
-#include "BKE_texture.h"
-#include "BKE_utildefines.h"
-#include "BKE_colortools.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
@@ -71,7 +64,6 @@
#include "WM_api.h"
#include "WM_types.h"
#include "ED_screen.h"
-#include "ED_sculpt.h"
#include "ED_view3d.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -81,15 +73,84 @@
#include "RE_render_ext.h"
+#include "RE_shader_ext.h"
-#include "gpu_buffers.h"
+#include "GPU_buffers.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
-/* Number of vertices to average in order to determine the flatten distance */
-#define FLATTEN_SAMPLE_SIZE 10
+#ifdef _OPENMP
+#include <omp.h>
+#endif
+
+/* ==== FORWARD DEFINITIONS =====
+ *
+ */
+
+void ED_sculpt_force_update(bContext *C)
+{
+ Object *ob= CTX_data_active_object(C);
+
+ if(ob && (ob->mode & OB_MODE_SCULPT))
+ multires_force_update(ob);
+}
+
+/* Sculpt mode handles multires differently from regular meshes, but only if
+ it's the last modifier on the stack and it is not on the first level */
+struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob)
+{
+ Mesh *me= (Mesh*)ob->data;
+ ModifierData *md, *nmd;
+
+ if(!CustomData_get_layer(&me->fdata, CD_MDISPS)) {
+ /* multires can't work without displacement layer */
+ return NULL;
+ }
+
+ for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
+ if(md->type == eModifierType_Multires) {
+ MultiresModifierData *mmd= (MultiresModifierData*)md;
+
+ /* Check if any of the modifiers after multires are active
+ * if not it can use the multires struct */
+ for(nmd= md->next; nmd; nmd= nmd->next)
+ if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime))
+ break;
+
+ if(!nmd && mmd->sculptlvl > 0)
+ return mmd;
+ }
+ }
+
+ return NULL;
+}
+
+/* Checks whether full update mode (slower) needs to be used to work with modifiers */
+int sculpt_modifiers_active(Scene *scene, Object *ob)
+{
+ ModifierData *md;
+ MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
+
+ /* check if there are any modifiers after what we are sculpting,
+ for a multires modifier with a deform modifier in front, we
+ do no need to recalculate the modifier stack. note that this
+ needs to be in sync with ccgDM_use_grid_pbvh! */
+ if(mmd)
+ md= mmd->modifier.next;
+ else
+ md= modifiers_getVirtualModifierList(ob);
+
+ /* exception for shape keys because we can edit those */
+ for(; md; md= md->next) {
+ if(modifier_isEnabled(scene, md, eModifierMode_Realtime))
+ if(md->type != eModifierType_ShapeKey)
+ return 1;
+ }
+
+ return 0;
+}
/* ===== STRUCTS =====
*
@@ -116,10 +177,13 @@ typedef struct StrokeCache {
/* Variants */
float radius;
+ float radius_squared;
+ //float traced_location[3];
float true_location[3];
float location[3];
- float flip;
+ float pen_flip;
+ float invert;
float pressure;
float mouse[2];
float bstrength;
@@ -136,15 +200,30 @@ typedef struct StrokeCache {
Brush *brush;
float (*face_norms)[3]; /* Copy of the mesh faces' normals */
- float rotation; /* Texture rotation (radians) for anchored and rake modes */
+ float special_rotation; /* Texture rotation (radians) for anchored and rake modes */
int pixel_radius, previous_pixel_radius;
- float grab_active_location[8][3];
float grab_delta[3], grab_delta_symmetry[3];
float old_grab_location[3], orig_grab_location[3];
- int symmetry; /* Symmetry index between 0 and 7 */
- float view_normal[3], view_normal_symmetry[3];
- int last_rake[2]; /* Last location of updating rake rotation */
+
+ int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only;
+ 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
+ int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/
+ float true_view_normal[3];
+ float view_normal[3];
+ float last_area_normal[3];
+ float last_center[3];
+ 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 vertex_rotation;
+
+ char saved_active_brush_name[24];
+ int alt_smooth;
+
+ float plane_trim_squared;
} StrokeCache;
/* ===== OPENGL =====
@@ -219,10 +298,12 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar,
RegionView3D *rv3d, Object *ob)
{
PBVH *pbvh= ob->sculpt->pbvh;
- BoundBox *bb = MEM_callocN(sizeof(BoundBox), "sculpt boundbox");
+ BoundBox bb;
bglMats mats;
rcti rect;
+ memset(&bb, 0, sizeof(BoundBox));
+
view3d_get_transformation(ar, rv3d, ob, &mats);
sculpt_get_redraw_rect(ar, rv3d,ob, &rect);
@@ -241,353 +322,354 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar,
rect.ymax -= 2;
#endif
- view3d_calculate_clipping(bb, planes, &mats, &rect);
+ view3d_calculate_clipping(&bb, planes, &mats, &rect);
mul_m4_fl(planes, -1.0f);
- MEM_freeN(bb);
-
/* clear redraw flag from nodes */
if(pbvh)
BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL);
}
-/************************** Undo *************************/
-
-typedef struct SculptUndoNode {
- struct SculptUndoNode *next, *prev;
-
- char idname[MAX_ID_NAME]; /* name instead of pointer*/
- void *node; /* only during push, not valid afterwards! */
-
- float (*co)[3];
- short (*no)[3];
- int totvert;
-
- /* non-multires */
- int maxvert; /* to verify if totvert it still the same */
- int *index; /* to restore into right location */
-
- /* multires */
- int maxgrid; /* same for grid */
- int gridsize; /* same for grid */
- int totgrid; /* to restore into right location */
- int *grids; /* to restore into right location */
+/************************ Brush Testing *******************/
- /* layer brush */
- float *layer_disp;
-} SculptUndoNode;
+typedef struct SculptBrushTest {
+ float radius_squared;
+ float location[3];
+ float dist;
+} SculptBrushTest;
-static void update_cb(PBVHNode *node, void *data)
+static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
{
- BLI_pbvh_node_mark_update(node);
+ test->radius_squared= ss->cache->radius_squared;
+ copy_v3_v3(test->location, ss->cache->location);
}
-/* Checks whether full update mode (slower) needs to be used to work with modifiers */
-static int sculpt_modifiers_active(Scene *scene, Object *ob)
+static int sculpt_brush_test(SculptBrushTest *test, float co[3])
{
- ModifierData *md;
-
- for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
- if(modifier_isEnabled(scene, md, eModifierMode_Realtime))
- if(!ELEM(md->type, eModifierType_Multires, eModifierType_ShapeKey))
- return 1;
+ float distsq = len_squared_v3v3(co, test->location);
+
+ if(distsq <= test->radius_squared) {
+ test->dist = sqrt(distsq);
+ return 1;
+ }
+ else {
+ return 0;
}
-
- return 0;
}
-static void sculpt_undo_restore(bContext *C, ListBase *lb)
+static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3])
{
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
- DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
- SculptSession *ss = ob->sculpt;
- SculptUndoNode *unode;
- MVert *mvert;
- MultiresModifierData *mmd;
- int *index;
- int i, j, update= 0;
-
- sculpt_update_mesh_elements(scene, ob, 0);
-
- for(unode=lb->first; unode; unode=unode->next) {
- if(!(strcmp(unode->idname, ob->id.name)==0))
- continue;
+ float distsq = len_squared_v3v3(co, test->location);
- if(unode->maxvert) {
- /* regular mesh restore */
- if(ss->totvert != unode->maxvert)
- continue;
+ if(distsq <= test->radius_squared) {
+ test->dist = distsq;
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
- index= unode->index;
- mvert= ss->mvert;
+static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3])
+{
+ return len_squared_v3v3(co, test->location) <= test->radius_squared;
+}
- for(i=0; i<unode->totvert; i++) {
- swap_v3_v3(mvert[index[i]].co, unode->co[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- else if(unode->maxgrid && dm->getGridData) {
- /* multires restore */
- DMGridData **grids, *grid;
- float (*co)[3];
- int gridsize;
+static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4])
+{
+ static const float side = 0.70710678118654752440084436210485; // sqrt(.5);
- if(dm->getNumGrids(dm) != unode->maxgrid)
- continue;
- if(dm->getGridSize(dm) != unode->gridsize)
- continue;
+ float local_co[3];
- grids= dm->getGridData(dm);
- gridsize= dm->getGridSize(dm);
+ mul_v3_m4v3(local_co, local, co);
- co = unode->co;
- for(j=0; j<unode->totgrid; j++) {
- grid= grids[unode->grids[j]];
+ local_co[0] = fabs(local_co[0]);
+ local_co[1] = fabs(local_co[1]);
+ local_co[2] = fabs(local_co[2]);
- for(i=0; i<gridsize*gridsize; i++, co++)
- swap_v3_v3(grid[i].co, co[0]);
- }
- }
+ if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) {
+ test->dist = MAX3(local_co[0], local_co[1], local_co[2]) / side;
- update= 1;
+ return 1;
+ }
+ else {
+ return 0;
}
+}
- if(update) {
- if(ss->kb) sculpt_mesh_to_key(ss->ob, ss->kb);
- if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob);
+static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[3])
+{
+ if (brush->flag & BRUSH_FRONTFACE) {
+ float dot;
- /* we update all nodes still, should be more clever, but also
- needs to work correct when exiting/entering sculpt mode and
- the nodes get recreated, though in that case it could do all */
- BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, NULL);
- BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL);
+ if (no) {
+ float tmp[3];
- if((mmd=sculpt_multires_active(ob)))
- multires_mark_as_modified(ob);
+ normal_short_to_float_v3(tmp, no);
+ dot= dot_v3v3(tmp, sculpt_normal);
+ }
+ else {
+ dot= dot_v3v3(fno, sculpt_normal);
+ }
- if(sculpt_modifiers_active(scene, ob))
- DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+ return dot > 0 ? dot : 0;
+ }
+ else {
+ return 1;
}
}
-static void sculpt_undo_free(ListBase *lb)
+#if 0
+
+static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3])
{
- SculptUndoNode *unode;
+ if (sculpt_brush_test_fast(test, co)) {
+ float t1[3], t2[3], t3[3], dist;
- for(unode=lb->first; unode; unode=unode->next) {
- if(unode->co)
- MEM_freeN(unode->co);
- if(unode->no)
- MEM_freeN(unode->no);
- if(unode->index)
- MEM_freeN(unode->index);
- if(unode->grids)
- MEM_freeN(unode->grids);
- if(unode->layer_disp)
- MEM_freeN(unode->layer_disp);
- }
-}
+ sub_v3_v3v3(t1, location, co);
+ sub_v3_v3v3(t2, x2, location);
-static SculptUndoNode *sculpt_undo_get_node(SculptSession *ss, PBVHNode *node)
-{
- ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
- SculptUndoNode *unode;
+ cross_v3_v3v3(t3, an, t1);
- if(!lb)
- return NULL;
+ dist = len_v3(t3)/len_v3(t2);
- for(unode=lb->first; unode; unode=unode->next)
- if(unode->node == node)
- return unode;
+ test->dist = dist;
- return NULL;
+ return 1;
+ }
+
+ return 0;
}
-static SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node)
-{
- ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
- Object *ob= ss->ob;
- SculptUndoNode *unode;
- int totvert, allvert, totgrid, maxgrid, gridsize, *grids;
+#endif
- /* list is manipulated by multiple threads, so we lock */
- BLI_lock_thread(LOCK_CUSTOM1);
+/* ===== Sculpting =====
+ *
+ */
+
- if((unode= sculpt_undo_get_node(ss, node))) {
- BLI_unlock_thread(LOCK_CUSTOM1);
- return unode;
- }
+static float overlapped_curve(Brush* br, float x)
+{
+ int i;
+ const int n = 100 / br->spacing;
+ const float h = br->spacing / 50.0f;
+ const float x0 = x-1;
- unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode");
- strcpy(unode->idname, ob->id.name);
- unode->node= node;
+ float sum;
- BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert);
- BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid,
- &maxgrid, &gridsize, NULL, NULL);
+ sum = 0;
+ for (i= 0; i < n; i++) {
+ float xx;
- unode->totvert= totvert;
- /* we will use this while sculpting, is mapalloc slow to access then? */
- unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co");
- unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no");
- undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert);
- BLI_addtail(lb, unode);
+ xx = fabs(x0 + i*h);
- if(maxgrid) {
- /* multires */
- unode->maxgrid= maxgrid;
- unode->totgrid= totgrid;
- unode->gridsize= gridsize;
- unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids");
+ if (xx < 1.0f)
+ sum += brush_curve_strength(br, xx, 1);
}
- else {
- /* regular mesh */
- unode->maxvert= ss->totvert;
- unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index");
- }
-
- BLI_unlock_thread(LOCK_CUSTOM1);
- /* copy threaded, hopefully this is the performance critical part */
- {
- PBVHVertexIter vd;
+ return sum;
+}
- BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) {
- copy_v3_v3(unode->co[vd.i], vd.co);
- if(vd.no) VECCOPY(unode->no[vd.i], vd.no)
- else normal_float_to_short_v3(unode->no[vd.i], vd.fno);
- if(vd.vert_indices) unode->index[vd.i]= vd.vert_indices[vd.i];
- }
- BLI_pbvh_vertex_iter_end;
+static float integrate_overlap(Brush* br)
+{
+ int i;
+ int m= 10;
+ float g = 1.0f/m;
+ float overlap;
+ float max;
+
+ overlap= 0;
+ max= 0;
+ for(i= 0; i < m; i++) {
+ overlap = overlapped_curve(br, i*g);
+
+ if (overlap > max)
+ max = overlap;
}
- if(unode->grids)
- memcpy(unode->grids, grids, sizeof(int)*totgrid);
-
- return unode;
+ return max;
}
-static void sculpt_undo_push_begin(SculptSession *ss, char *name)
+/* Uses symm to selectively flip any axis of a coordinate. */
+static void flip_coord(float out[3], float in[3], const char symm)
{
- undo_paint_push_begin(UNDO_PAINT_MESH, name,
- sculpt_undo_restore, sculpt_undo_free);
+ if(symm & SCULPT_SYMM_X)
+ out[0]= -in[0];
+ else
+ out[0]= in[0];
+ if(symm & SCULPT_SYMM_Y)
+ out[1]= -in[1];
+ else
+ out[1]= in[1];
+ if(symm & SCULPT_SYMM_Z)
+ out[2]= -in[2];
+ else
+ out[2]= in[2];
}
-static void sculpt_undo_push_end(SculptSession *ss)
+float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle)
{
- ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
- SculptUndoNode *unode;
-
- /* we don't need normals in the undo stack */
- for(unode=lb->first; unode; unode=unode->next) {
- if(unode->no) {
- MEM_freeN(unode->no);
- unode->no= NULL;
- }
+ float mirror[3];
+ float distsq;
+ float mat[4][4];
+
+ //flip_coord(mirror, cache->traced_location, symm);
+ flip_coord(mirror, cache->true_location, symm);
- if(unode->layer_disp) {
- MEM_freeN(unode->layer_disp);
- unode->layer_disp= NULL;
- }
- }
+ unit_m4(mat);
+ rotate_m4(mat, axis, angle);
- undo_paint_push_end(UNDO_PAINT_MESH);
-}
+ mul_m4_v3(mat, mirror);
-void ED_sculpt_force_update(bContext *C)
-{
- Object *ob= CTX_data_active_object(C);
+ //distsq = len_squared_v3v3(mirror, cache->traced_location);
+ distsq = len_squared_v3v3(mirror, cache->true_location);
- if(ob && (ob->mode & OB_MODE_SCULPT))
- multires_force_update(ob);
+ if (distsq <= 4*(cache->radius_squared))
+ return (2*(cache->radius) - sqrt(distsq)) / (2*(cache->radius));
+ else
+ return 0;
}
-/************************ Brush Testing *******************/
-
-typedef struct SculptBrushTest {
- float radius_squared;
- float location[3];
+static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis)
+{
+ int i;
+ float overlap;
- float dist;
-} SculptBrushTest;
+ overlap = 0;
+ for(i = 1; i < sd->radial_symm[axis-'X']; ++i) {
+ const float angle = 2*M_PI*i/sd->radial_symm[axis-'X'];
+ overlap += calc_overlap(cache, symm, axis, angle);
+ }
-static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
-{
- test->radius_squared= ss->cache->radius*ss->cache->radius;
- copy_v3_v3(test->location, ss->cache->location);
+ return overlap;
}
-static int sculpt_brush_test(SculptBrushTest *test, float co[3])
+static float calc_symmetry_feather(Sculpt *sd, StrokeCache* cache)
{
- float distsq, delta[3];
+ if (sd->flags & SCULPT_SYMMETRY_FEATHER) {
+ float overlap;
+ int symm = cache->symmetry;
+ int i;
- sub_v3_v3v3(delta, co, test->location);
- distsq = INPR(delta, delta);
+ overlap = 0;
+ for (i = 0; i <= symm; i++) {
+ if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
- if(distsq < test->radius_squared) {
- test->dist = sqrt(distsq);
+ overlap += calc_overlap(cache, i, 0, 0);
+
+ overlap += calc_radial_symmetry_feather(sd, cache, i, 'X');
+ overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y');
+ overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z');
+ }
+ }
+
+ return 1/overlap;
+ }
+ else {
return 1;
}
-
- return 0;
}
-/* ===== Sculpting =====
- *
- */
-
/* Return modified brush strength. Includes the direction of the brush, positive
values pull vertices, negative values push. Uses tablet pressure and a
special multiplier found experimentally to scale the strength factor. */
-static float brush_strength(Sculpt *sd, StrokeCache *cache)
+static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather)
{
Brush *brush = paint_brush(&sd->paint);
- /* Primary strength input; square it to make lower values more sensitive */
- float alpha = brush->alpha * brush->alpha;
- float dir= brush->flag & BRUSH_DIR_IN ? -1 : 1;
- float pressure= 1;
- float flip= cache->flip ? -1:1;
+ /* Primary strength input; square it to make lower values more sensitive */
+ const float root_alpha = brush_alpha(brush);
+ float alpha = root_alpha*root_alpha;
+ float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1;
+ float pressure = brush_use_alpha_pressure(brush) ? cache->pressure : 1;
+ float pen_flip = cache->pen_flip ? -1 : 1;
+ float invert = cache->invert ? -1 : 1;
+ float accum = integrate_overlap(brush);
+ float overlap = (brush->flag & BRUSH_SPACE_ATTEN && brush->flag & BRUSH_SPACE && !(brush->flag & BRUSH_ANCHORED)) && (brush->spacing < 100) ? 1.0f/accum : 1; // spacing is integer percentage of radius, divide by 50 to get normalized diameter
+ float flip = dir * invert * pen_flip;
- if(brush->flag & BRUSH_ALPHA_PRESSURE)
- pressure *= cache->pressure;
-
switch(brush->sculpt_tool){
- case SCULPT_TOOL_DRAW:
- case SCULPT_TOOL_INFLATE:
- case SCULPT_TOOL_CLAY:
- case SCULPT_TOOL_FLATTEN:
- case SCULPT_TOOL_LAYER:
- return alpha * dir * pressure * flip; /*XXX: not sure why? was multiplied by G.vd->grid */;
- case SCULPT_TOOL_SMOOTH:
- return alpha * 4 * pressure;
- case SCULPT_TOOL_PINCH:
- return alpha / 2 * dir * pressure * flip;
- case SCULPT_TOOL_GRAB:
- return 1;
- default:
- return 0;
+ case SCULPT_TOOL_CLAY:
+ case SCULPT_TOOL_CLAY_TUBES:
+ case SCULPT_TOOL_DRAW:
+ case SCULPT_TOOL_LAYER:
+ return alpha * flip * pressure * overlap * feather;
+
+ case SCULPT_TOOL_CREASE:
+ case SCULPT_TOOL_BLOB:
+ return alpha * flip * pressure * overlap * feather;
+
+ case SCULPT_TOOL_INFLATE:
+ if (flip > 0) {
+ return 0.250f * alpha * flip * pressure * overlap * feather;
+ }
+ else {
+ return 0.125f * alpha * flip * pressure * overlap * feather;
+ }
+
+ case SCULPT_TOOL_FILL:
+ case SCULPT_TOOL_SCRAPE:
+ case SCULPT_TOOL_FLATTEN:
+ if (flip > 0) {
+ overlap = (1+overlap) / 2;
+ return alpha * flip * pressure * overlap * feather;
+ }
+ else {
+ /* reduce strength for DEEPEN, PEAKS, and CONTRAST */
+ return 0.5f * alpha * flip * pressure * overlap * feather;
+ }
+
+ case SCULPT_TOOL_SMOOTH:
+ return alpha * pressure * feather;
+
+ case SCULPT_TOOL_PINCH:
+ if (flip > 0) {
+ return alpha * flip * pressure * overlap * feather;
+ }
+ else {
+ return 0.25f * alpha * flip * pressure * overlap * feather;
+ }
+
+ case SCULPT_TOOL_NUDGE:
+ overlap = (1+overlap) / 2;
+ return alpha * pressure * overlap * feather;
+
+ case SCULPT_TOOL_THUMB:
+ return alpha*pressure*feather;
+
+ case SCULPT_TOOL_SNAKE_HOOK:
+ return feather;
+
+ case SCULPT_TOOL_GRAB:
+ case SCULPT_TOOL_ROTATE:
+ return feather;
+
+ default:
+ return 0;
}
}
-/* Uses symm to selectively flip any axis of a coordinate. */
-static void flip_coord(float out[3], float in[3], const char symm)
+float get_tex_pixel(Brush* br, float u, float v)
{
- if(symm & SCULPT_SYMM_X)
- out[0]= -in[0];
- else
- out[0]= in[0];
- if(symm & SCULPT_SYMM_Y)
- out[1]= -in[1];
- else
- out[1]= in[1];
- if(symm & SCULPT_SYMM_Z)
- out[2]= -in[2];
- else
- out[2]= in[2];
+ TexResult texres;
+ float co[3];
+ int hasrgb;
+
+ co[0] = u;
+ co[1] = v;
+ co[2] = 0;
+
+ memset(&texres, 0, sizeof(TexResult));
+ hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres);
+
+ if (hasrgb & TEX_RGB)
+ texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta;
+
+ return texres.tin;
}
+#if 0
+
/* Get a pixel from the texcache at (px, py) */
static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py)
{
@@ -598,7 +680,7 @@ static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py)
static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v)
{
- int x, y, x2, y2;
+ unsigned x, y, x2, y2;
const int tc_max = ss->texcache_side - 1;
float urat, vrat, uopp;
@@ -625,76 +707,97 @@ static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float
get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0;
}
+#endif
+
/* Return a multiplier for brush strength on a particular vertex. */
static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len)
{
- MTex *tex = &br->mtex;
+ MTex *mtex = &br->mtex;
float avg= 1;
- if(!tex) {
+ if(!mtex->tex) {
avg= 1;
}
- else if(tex->brush_map_mode == MTEX_MAP_MODE_3D) {
+ else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
float jnk;
/* Get strength by feeding the vertex
location directly into a texture */
- externtex(tex, point, &avg,
+ externtex(mtex, point, &avg,
&jnk, &jnk, &jnk, &jnk);
}
else if(ss->texcache) {
- const float bsize= ss->cache->pixel_radius * 2;
- const float rot= tex->rot + ss->cache->rotation;
- int px, py;
- float flip[3], point_2d[2];
-
- /* If the active area is being applied for symmetry, flip it
- across the symmetry axis in order to project it. This insures
- that the brush texture will be oriented correctly. */
- copy_v3_v3(flip, point);
- flip_coord(flip, flip, ss->cache->symmetry);
- projectf(ss->cache->mats, flip, point_2d);
-
- /* For Tile and Drag modes, get the 2D screen coordinates of the
- and scale them up or down to the texture size. */
- if(tex->brush_map_mode == MTEX_MAP_MODE_TILED) {
- const int sx= (const int)tex->size[0];
- const int sy= (const int)tex->size[1];
-
- float fx= point_2d[0];
- float fy= point_2d[1];
-
- float angle= atan2(fy, fx) - rot;
- float flen= sqrtf(fx*fx + fy*fy);
-
- if(rot<0.001 && rot>-0.001) {
- px= point_2d[0];
- py= point_2d[1];
- } else {
- px= flen * cos(angle) + 2000;
- py= flen * sin(angle) + 2000;
- }
- if(sx != 1)
- px %= sx-1;
- if(sy != 1)
- py %= sy-1;
- avg= get_texcache_pixel_bilinear(ss, ss->texcache_side*px/sx, ss->texcache_side*py/sy);
- }
- else if(tex->brush_map_mode == MTEX_MAP_MODE_FIXED) {
- float fx= (point_2d[0] - ss->cache->tex_mouse[0]) / bsize;
- float fy= (point_2d[1] - ss->cache->tex_mouse[1]) / bsize;
-
- float angle= atan2(fy, fx) - rot;
- float flen= sqrtf(fx*fx + fy*fy);
-
- fx = flen * cos(angle) + 0.5;
- fy = flen * sin(angle) + 0.5;
+ float rotation = -mtex->rot;
+ float x, y, point_2d[3];
+ float radius;
+
+ /* if the active area is being applied for symmetry, flip it
+ across the symmetry axis and rotate it back to the orignal
+ position in order to project it. This insures that the
+ brush texture will be oriented correctly. */
+
+ flip_coord(point_2d, point, ss->cache->mirror_symmetry_pass);
+
+ if (ss->cache->radial_symmetry_pass)
+ mul_m4_v3(ss->cache->symm_rot_mat_inv, point_2d);
+
+ projectf(ss->cache->mats, point_2d, point_2d);
+
+ /* if fixed mode, keep coordinates relative to mouse */
+ if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) {
+ rotation += ss->cache->special_rotation;
+
+ point_2d[0] -= ss->cache->tex_mouse[0];
+ point_2d[1] -= ss->cache->tex_mouse[1];
- avg= get_texcache_pixel_bilinear(ss, fx * ss->texcache_side, fy * ss->texcache_side);
+ radius = ss->cache->pixel_radius; // use pressure adjusted size for fixed mode
+
+ x = point_2d[0];
+ y = point_2d[1];
+ }
+ else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED),
+ leave the coordinates relative to the screen */
+ {
+ radius = brush_size(br); // use unadjusted size for tiled mode
+
+ x = point_2d[0] - ss->cache->vc->ar->winrct.xmin;
+ y = point_2d[1] - ss->cache->vc->ar->winrct.ymin;
+ }
+
+ 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.001 || rotation < -0.001) {
+ const float angle = atan2(y, x) + rotation;
+ const float flen = sqrtf(x*x + y*y);
+
+ x = flen * cos(angle);
+ y = flen * sin(angle);
}
+
+ x *= br->mtex.size[0];
+ y *= br->mtex.size[1];
+
+ x += br->mtex.ofs[0];
+ y += br->mtex.ofs[1];
+
+ avg = get_tex_pixel(br, x, y);
}
- avg*= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */
+ avg += br->texture_sample_bias;
+
+ avg *= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */
return avg;
}
@@ -730,7 +833,7 @@ static int sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
sub_v3_v3v3(t, center, nearest);
- return t[0] * t[0] + t[1] * t[1] + t[2] * t[2] < data->radius_squared;
+ return dot_v3v3(t, t) < data->radius_squared;
}
/* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
@@ -746,64 +849,60 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float va
co[i]= 0.0f;
else
co[i]= val[i];
- }
+ }
}
static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3])
{
if((dot_v3v3(view_vec, fno)) > 0) {
- add_v3_v3v3(out, out, fno);
+ add_v3_v3(out, fno);
} else {
- add_v3_v3v3(out_flip, out_flip, fno); /* out_flip is used when out is {0,0,0} */
+ add_v3_v3(out_flip, fno); /* out_flip is used when out is {0,0,0} */
}
}
-/* For draw/layer/flatten; finds average normal for all active vertices */
-static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3], PBVHNode **nodes, int totnode)
+static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode)
{
- PBVH *bvh= ss->pbvh;
- StrokeCache *cache = ss->cache;
- const int view = 0; /* XXX: should probably be a flag, not number: brush_type==SCULPT_TOOL_DRAW ? sculptmode_brush()->view : 0; */
- float out[3] = {0.0f, 0.0f, 0.0f};
- float out_flip[3] = {0.0f, 0.0f, 0.0f};
- float out_dir[3];
int n;
- copy_v3_v3(out_dir, cache->view_normal_symmetry);
+ float out_flip[3] = {0.0f, 0.0f, 0.0f};
- /* threaded loop over nodes */
- #pragma omp parallel for private(n) schedule(static)
+ zero_v3(an);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
SculptUndoNode *unode;
- float fno[3];
- float nout[3] = {0.0f, 0.0f, 0.0f};
- float nout_flip[3] = {0.0f, 0.0f, 0.0f};
-
- // XXX push instead of get for thread safety in draw
- // brush .. lame, but also not harmful really
- unode= sculpt_undo_push_node(ss, nodes[n]);
+ float private_an[3] = {0.0f, 0.0f, 0.0f};
+ float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
+
+ unode = sculpt_undo_push_node(ss, nodes[n]);
sculpt_brush_test_init(ss, &test);
if(ss->cache->original) {
- BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test(&test, unode->co[vd.i])) {
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+ float fno[3];
+
normal_short_to_float_v3(fno, unode->no[vd.i]);
- add_norm_if(out_dir, nout, nout_flip, fno);
+ add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
}
}
BLI_pbvh_vertex_iter_end;
}
else {
- BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test_fast(&test, vd.co)) {
if(vd.no) {
+ float fno[3];
+
normal_short_to_float_v3(fno, vd.no);
- add_norm_if(out_dir, nout, nout_flip, fno);
+ add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
+ }
+ else {
+ add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno);
}
- else
- add_norm_if(out_dir, nout, nout_flip, vd.fno);
}
}
BLI_pbvh_vertex_iter_end;
@@ -811,72 +910,70 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3]
#pragma omp critical
{
- /* we sum per node and add together later for threads */
- add_v3_v3v3(out, out, nout);
- add_v3_v3v3(out_flip, out_flip, nout_flip);
+ add_v3_v3(an, private_an);
+ add_v3_v3(out_flip, private_out_flip);
}
}
- if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) {
- copy_v3_v3(out, out_flip);
- }
-
- normalize_v3(out);
+ if (is_zero_v3(an))
+ copy_v3_v3(an, out_flip);
- out[0] = out_dir[0] * view + out[0] * (10-view);
- out[1] = out_dir[1] * view + out[1] * (10-view);
- out[2] = out_dir[2] * view + out[2] * (10-view);
-
- normalize_v3(out);
- copy_v3_v3(area_normal, out);
+ normalize_v3(an);
}
-static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+/* This initializes the faces to be moved for this sculpt for draw/layer/flatten; then it
+ finds average normal for all active vertices - note that this is called once for each mirroring direction */
+static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode)
{
Brush *brush = paint_brush(&sd->paint);
- float offset[3], area_normal[3];
- float bstrength= ss->cache->bstrength;
- int n;
-
- /* area normal */
- calc_area_normal(sd, ss, area_normal, nodes, totnode);
- /* offset with as much as possible factored in already */
- offset[0]= area_normal[0]*ss->cache->radius*ss->cache->scale[0]*bstrength;
- offset[1]= area_normal[1]*ss->cache->radius*ss->cache->scale[1]*bstrength;
- offset[2]= area_normal[2]*ss->cache->radius*ss->cache->scale[2]*bstrength;
-
- /* threaded loop over nodes */
- #pragma omp parallel for private(n) schedule(static)
- for(n=0; n<totnode; n++) {
- PBVHVertexIter vd;
- SculptBrushTest test;
-
- sculpt_undo_push_node(ss, nodes[n]);
- sculpt_brush_test_init(ss, &test);
-
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test(&test, vd.co)) {
- /* offset vertex */
- float fade = tex_strength(ss, brush, vd.co, test.dist);
- float val[3]= {vd.co[0] + offset[0]*fade,
- vd.co[1] + offset[1]*fade,
- vd.co[2] + offset[2]*fade};
-
- sculpt_clip(sd, ss, vd.co, val);
- if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
+ if (ss->cache->mirror_symmetry_pass == 0 &&
+ ss->cache->radial_symmetry_pass == 0 &&
+ (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
+ {
+ switch (brush->sculpt_plane) {
+ case SCULPT_DISP_DIR_VIEW:
+ viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an);
+ break;
+
+ case SCULPT_DISP_DIR_X:
+ an[1] = 0.0;
+ an[2] = 0.0;
+ an[0] = 1.0;
+ break;
+
+ case SCULPT_DISP_DIR_Y:
+ an[0] = 0.0;
+ an[2] = 0.0;
+ an[1] = 1.0;
+ break;
+
+ case SCULPT_DISP_DIR_Z:
+ an[0] = 0.0;
+ an[1] = 0.0;
+ an[2] = 1.0;
+ break;
+
+ case SCULPT_DISP_DIR_AREA:
+ calc_area_normal(sd, ss, an, nodes, totnode);
+
+ default:
+ break;
}
- BLI_pbvh_vertex_iter_end;
- BLI_pbvh_node_mark_update(nodes[n]);
+ copy_v3_v3(ss->cache->last_area_normal, an);
+ }
+ else {
+ copy_v3_v3(an, ss->cache->last_area_normal);
+ flip_coord(an, an, ss->cache->mirror_symmetry_pass);
+ mul_m4_v3(ss->cache->symm_rot_mat, an);
}
}
/* For the smooth brush, uses the neighboring vertices around vert to calculate
a smoothed location for vert. Skips corner vertices (used by only one
polygon.) */
-static void neighbor_average(SculptSession *ss, float avg[3], const int vert)
+static void neighbor_average(SculptSession *ss, float avg[3], const unsigned vert)
{
int i, skip= -1, total=0;
IndexNode *node= ss->fmap[vert].first;
@@ -903,7 +1000,7 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert)
for(i=0; i<(f->v4?4:3); ++i) {
if(i != skip && (ncount!=2 || BLI_countlist(&ss->fmap[(&f->v1)[i]]) <= 2)) {
- add_v3_v3v3(avg, avg, ss->mvert[(&f->v1)[i]].co);
+ add_v3_v3(avg, ss->mvert[(&f->v1)[i]].co);
++total;
}
}
@@ -917,52 +1014,58 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert)
copy_v3_v3(avg, ss->mvert[vert].co);
}
-static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node)
+static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength)
{
Brush *brush = paint_brush(&sd->paint);
- float bstrength= ss->cache->bstrength;
PBVHVertexIter vd;
SculptBrushTest test;
+ CLAMP(bstrength, 0.0f, 1.0f);
+
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if(sculpt_brush_test(&test, vd.co)) {
- float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
float avg[3], val[3];
- CLAMP(fade, 0.0f, 1.0f);
-
neighbor_average(ss, avg, vd.vert_indices[vd.i]);
- val[0] = vd.co[0]+(avg[0]-vd.co[0])*fade;
- val[1] = vd.co[1]+(avg[1]-vd.co[1])*fade;
- val[2] = vd.co[2]+(avg[2]-vd.co[2])*fade;
-
- sculpt_clip(sd, ss, vd.co, val);
- if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ sub_v3_v3v3(val, avg, vd.co);
+ mul_v3_fl(val, fade);
+
+ add_v3_v3(val, vd.co);
+
+ sculpt_clip(sd, ss, vd.co, val);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
}
-static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node)
+static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength)
{
Brush *brush = paint_brush(&sd->paint);
SculptBrushTest test;
DMGridData **griddata, *data;
DMGridAdjacency *gridadj, *adj;
- float bstrength= ss->cache->bstrength;
- float co[3], (*tmpgrid)[3];
+ float (*tmpgrid)[3], (*tmprow)[3];
int v1, v2, v3, v4;
int *grid_indices, totgrid, gridsize, i, x, y;
-
+
sculpt_brush_test_init(ss, &test);
+ CLAMP(bstrength, 0.0f, 1.0f);
+
BLI_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid,
NULL, &gridsize, &griddata, &gridadj);
#pragma omp critical
- tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid");
+ {
+ tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid");
+ tmprow= MEM_mallocN(sizeof(float)*3*gridsize, "tmprow");
+ }
for(i = 0; i < totgrid; ++i) {
data = griddata[grid_indices[i]];
@@ -970,75 +1073,106 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
memset(tmpgrid, 0, sizeof(float)*3*gridsize*gridsize);
- /* average grid values */
- for(y = 0; y < gridsize-1; ++y) {
- for(x = 0; x < gridsize-1; ++x) {
+ for (y= 0; y < gridsize-1; y++) {
+ float tmp[3];
+
+ v1 = y*gridsize;
+ add_v3_v3v3(tmprow[0], data[v1].co, data[v1+gridsize].co);
+
+ for (x= 0; x < gridsize-1; x++) {
v1 = x + y*gridsize;
- v2 = (x + 1) + y*gridsize;
- v3 = (x + 1) + (y + 1)*gridsize;
- v4 = x + (y + 1)*gridsize;
+ v2 = v1 + 1;
+ v3 = v1 + gridsize;
+ v4 = v3 + 1;
- cent_quad_v3(co, data[v1].co, data[v2].co, data[v3].co, data[v4].co);
- mul_v3_fl(co, 0.25f);
+ add_v3_v3v3(tmprow[x+1], data[v2].co, data[v4].co);
+ add_v3_v3v3(tmp, tmprow[x+1], tmprow[x]);
- add_v3_v3(tmpgrid[v1], co);
- add_v3_v3(tmpgrid[v2], co);
- add_v3_v3(tmpgrid[v3], co);
- add_v3_v3(tmpgrid[v4], co);
+ add_v3_v3(tmpgrid[v1], tmp);
+ add_v3_v3(tmpgrid[v2], tmp);
+ add_v3_v3(tmpgrid[v3], tmp);
+ add_v3_v3(tmpgrid[v4], tmp);
}
}
/* blend with existing coordinates */
for(y = 0; y < gridsize; ++y) {
for(x = 0; x < gridsize; ++x) {
- if(x == 0 && adj->index[0] == -1) continue;
- if(x == gridsize - 1 && adj->index[2] == -1) continue;
- if(y == 0 && adj->index[3] == -1) continue;
- if(y == gridsize - 1 && adj->index[1] == -1) continue;
+ float *co;
+ float *fno;
+ int index;
+
+ if(x == 0 && adj->index[0] == -1)
+ continue;
+
+ if(x == gridsize - 1 && adj->index[2] == -1)
+ continue;
- copy_v3_v3(co, data[x + y*gridsize].co);
+ if(y == 0 && adj->index[3] == -1)
+ continue;
+
+ if(y == gridsize - 1 && adj->index[1] == -1)
+ continue;
+
+ index = x + y*gridsize;
+ co= data[index].co;
+ fno= data[index].no;
if(sculpt_brush_test(&test, co)) {
- float fade = tex_strength(ss, brush, co, test.dist)*bstrength;
- float avg[3], val[3];
+ const float fade = bstrength*tex_strength(ss, brush, co, test.dist)*frontface(brush, ss->cache->view_normal, NULL, fno);
+ float *avg, val[3];
+ float n;
+
+ avg = tmpgrid[x + y*gridsize];
+
+ n = 1/16.0f;
- copy_v3_v3(avg, tmpgrid[x + y*gridsize]);
if(x == 0 || x == gridsize - 1)
- mul_v3_fl(avg, 2.0f);
+ n *= 2;
+
if(y == 0 || y == gridsize - 1)
- mul_v3_fl(avg, 2.0f);
+ n *= 2;
+
+ mul_v3_fl(avg, n);
+
+ sub_v3_v3v3(val, avg, co);
+ mul_v3_fl(val, fade);
- CLAMP(fade, 0.0f, 1.0f);
+ add_v3_v3(val, co);
- val[0] = co[0]+(avg[0]-co[0])*fade;
- val[1] = co[1]+(avg[1]-co[1])*fade;
- val[2] = co[2]+(avg[2]-co[2])*fade;
-
- sculpt_clip(sd, ss, data[x + y*gridsize].co, val);
+ sculpt_clip(sd, ss, co, val);
}
}
}
}
#pragma omp critical
- MEM_freeN(tmpgrid);
+ {
+ MEM_freeN(tmpgrid);
+ MEM_freeN(tmprow);
+ }
}
-static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float bstrength)
{
- int iteration, n;
+ const int max_iterations = 4;
+ const float fract = 1.0f/max_iterations;
+ int iteration, n, count;
+ float last;
- for(iteration = 0; iteration < 2; ++iteration) {
- #pragma omp parallel for private(n) schedule(static)
- for(n=0; n<totnode; n++) {
- sculpt_undo_push_node(ss, nodes[n]);
+ CLAMP(bstrength, 0, 1);
- if(ss->multires)
- do_multires_smooth_brush(sd, ss, nodes[n]);
- else if(ss->fmap)
- do_mesh_smooth_brush(sd, ss, nodes[n]);
+ count = (int)(bstrength*max_iterations);
+ last = max_iterations*(bstrength - count*fract);
- BLI_pbvh_node_mark_update(nodes[n]);
+ for(iteration = 0; iteration <= count; ++iteration) {
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n=0; n<totnode; n++) {
+ if(ss->multires) {
+ do_multires_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last);
+ }
+ else if(ss->fmap)
+ do_mesh_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last);
}
if(ss->multires)
@@ -1046,69 +1180,378 @@ static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
}
}
-static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ smooth(sd, ss, nodes, totnode, ss->cache->bstrength);
+}
+
+static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
{
Brush *brush = paint_brush(&sd->paint);
+ float offset[3], area_normal[3];
float bstrength= ss->cache->bstrength;
int n;
- #pragma omp parallel for private(n) schedule(static)
+ calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
+
+ /* offset with as much as possible factored in already */
+ mul_v3_v3fl(offset, area_normal, ss->cache->radius);
+ mul_v3_v3(offset, ss->cache->scale);
+ mul_v3_fl(offset, bstrength);
+
+ /* threaded loop over nodes */
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
-
- sculpt_undo_push_node(ss, nodes[n]);
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test(&test, vd.co)) {
+ //if(sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, area_normal)) {
+ /* offset vertex */
+ float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
+
+ mul_v3_v3fl(proxy[vd.i], offset, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+}
+
+static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
+ float offset[3], area_normal[3];
+ float bstrength= ss->cache->bstrength;
+ float flippedbstrength, crease_correction;
+ int n;
+
+ calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
+
+ /* offset with as much as possible factored in already */
+ mul_v3_v3fl(offset, area_normal, ss->cache->radius);
+ mul_v3_v3(offset, ss->cache->scale);
+ mul_v3_fl(offset, bstrength);
+
+ /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */
+
+ if(brush_alpha(brush) > 0.0f)
+ crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor/(brush_alpha(brush)*brush_alpha(brush));
+ else
+ crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor;
+
+ /* we always want crease to pinch or blob to relax even when draw is negative */
+ flippedbstrength = (bstrength < 0) ? -crease_correction*bstrength : crease_correction*bstrength;
+
+ if(brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f;
+
+ /* threaded loop over nodes */
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n=0; n<totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
if(sculpt_brush_test(&test, vd.co)) {
- float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
- float val[3]= {vd.co[0]+(test.location[0]-vd.co[0])*fade,
- vd.co[1]+(test.location[1]-vd.co[1])*fade,
- vd.co[2]+(test.location[2]-vd.co[2])*fade};
+ /* offset vertex */
+ const float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
+ float val1[3];
+ float val2[3];
+
+ /* first we pinch */
+ sub_v3_v3v3(val1, test.location, vd.co);
+ //mul_v3_v3(val1, ss->cache->scale);
+ mul_v3_fl(val1, fade*flippedbstrength);
+
+ /* then we draw */
+ mul_v3_v3fl(val2, offset, fade);
+
+ add_v3_v3v3(proxy[vd.i], val1, val2);
- sculpt_clip(sd, ss, vd.co, val);
- if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
+ }
+}
+
+static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
+ float bstrength= ss->cache->bstrength;
+ int n;
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n=0; n<totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test(&test, vd.co)) {
+ float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
+ float val[3];
+
+ sub_v3_v3v3(val, test.location, vd.co);
+ mul_v3_v3fl(proxy[vd.i], val, fade);
- BLI_pbvh_node_mark_update(nodes[n]);
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
}
}
static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
{
- Brush *brush = paint_brush(&sd->paint);
+ Brush *brush= paint_brush(&sd->paint);
float bstrength= ss->cache->bstrength;
- float grab_delta[3];
+ float grab_delta[3], an[3];
int n;
-
+ float len;
+
+ if (brush->normal_weight > 0)
+ calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
- #pragma omp parallel for private(n) schedule(static)
+ len = len_v3(grab_delta);
+
+ if (brush->normal_weight > 0) {
+ mul_v3_fl(an, len*brush->normal_weight);
+ mul_v3_fl(grab_delta, 1.0f - brush->normal_weight);
+ add_v3_v3(grab_delta, an);
+ }
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
+ SculptUndoNode* unode;
SculptBrushTest test;
float (*origco)[3];
-
- origco= sculpt_undo_push_node(ss, nodes[n])->co;
+ short (*origno)[3];
+ float (*proxy)[3];
+
+ unode= sculpt_undo_push_node(ss, nodes[n]);
+ origco= unode->co;
+ origno= unode->no;
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test(&test, origco[vd.i])) {
+ const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL);
+
+ mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+}
+
+static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
+ float bstrength = ss->cache->bstrength;
+ float grab_delta[3];
+ int n;
+ float an[3];
+ float tmp[3], cono[3];
+
+ copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+
+ calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+ cross_v3_v3v3(tmp, an, grab_delta);
+ cross_v3_v3v3(cono, tmp, an);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n = 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test(&test, vd.co)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno);
+
+ mul_v3_v3fl(proxy[vd.i], cono, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+}
+
+static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
+ float bstrength = ss->cache->bstrength;
+ float grab_delta[3], an[3];
+ int n;
+ float len;
+
+ if (brush->normal_weight > 0)
+ calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+ copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+
+ len = len_v3(grab_delta);
+
+ if (bstrength < 0)
+ negate_v3(grab_delta);
+
+ if (brush->normal_weight > 0) {
+ mul_v3_fl(an, len*brush->normal_weight);
+ mul_v3_fl(grab_delta, 1.0f - brush->normal_weight);
+ add_v3_v3(grab_delta, an);
+ }
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n = 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test(&test, vd.co)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno);
+
+ mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+}
+
+static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
+ float bstrength = ss->cache->bstrength;
+ float grab_delta[3];
+ int n;
+ float an[3];
+ float tmp[3], cono[3];
+
+ copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+
+ calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+ cross_v3_v3v3(tmp, an, grab_delta);
+ cross_v3_v3v3(cono, tmp, an);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n = 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ SculptUndoNode* unode;
+ SculptBrushTest test;
+ float (*origco)[3];
+ short (*origno)[3];
+ float (*proxy)[3];
+
+ unode= sculpt_undo_push_node(ss, nodes[n]);
+ origco= unode->co;
+ origno= unode->no;
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
if(sculpt_brush_test(&test, origco[vd.i])) {
- float fade = tex_strength(ss, brush, origco[vd.i], test.dist)*bstrength;
- float add[3]= {vd.co[0]+fade*grab_delta[0],
- vd.co[1]+fade*grab_delta[1],
- vd.co[2]+fade*grab_delta[2]};
+ const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL);
+
+ mul_v3_v3fl(proxy[vd.i], cono, fade);
- sculpt_clip(sd, ss, vd.co, add);
- if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
+ }
+}
+
+static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush= paint_brush(&sd->paint);
+ float bstrength= ss->cache->bstrength;
+ float an[3];
+ int n;
+ float m[3][3];
+ static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 };
+ float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass];
+
+ calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+ axis_angle_to_mat3(m, an, angle);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n=0; n<totnode; n++) {
+ PBVHVertexIter vd;
+ SculptUndoNode* unode;
+ SculptBrushTest test;
+ float (*origco)[3];
+ short (*origno)[3];
+ float (*proxy)[3];
+
+ unode= sculpt_undo_push_node(ss, nodes[n]);
+ origco= unode->co;
+ origno= unode->no;
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test(&test, origco[vd.i])) {
+ const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL);
- BLI_pbvh_node_mark_update(nodes[n]);
+ mul_v3_m3v3(proxy[vd.i], m, origco[vd.i]);
+ sub_v3_v3(proxy[vd.i], origco[vd.i]);
+ mul_v3_fl(proxy[vd.i], fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
}
}
@@ -1120,63 +1563,66 @@ static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
float lim= ss->cache->radius / 4;
int n;
- if(ss->cache->flip)
+ if(bstrength < 0)
lim = -lim;
- calc_area_normal(sd, ss, area_normal, nodes, totnode);
+ calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
- offset[0]= ss->cache->scale[0]*area_normal[0];
- offset[1]= ss->cache->scale[1]*area_normal[1];
- offset[2]= ss->cache->scale[2]*area_normal[2];
+ mul_v3_v3v3(offset, ss->cache->scale, area_normal);
- #pragma omp parallel for private(n) schedule(static)
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
SculptUndoNode *unode;
float (*origco)[3], *layer_disp;
+ //float (*proxy)[3]; // XXX layer brush needs conversion to proxy but its more complicated
+
+ //proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
unode= sculpt_undo_push_node(ss, nodes[n]);
origco=unode->co;
if(!unode->layer_disp)
- unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp");
+ {
+ #pragma omp critical
+ unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp");
+ }
+
layer_disp= unode->layer_disp;
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
if(sculpt_brush_test(&test, vd.co)) {
- float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
+ const float fade = bstrength*ss->cache->radius*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
float *disp= &layer_disp[vd.i];
float val[3];
-
+
*disp+= fade;
-
+
/* Don't let the displacement go past the limit */
if((lim < 0 && *disp < lim) || (lim > 0 && *disp > lim))
*disp = lim;
-
+
+ mul_v3_v3fl(val, offset, *disp);
+
if(ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) {
int index= vd.vert_indices[vd.i];
/* persistent base */
- val[0] = ss->layer_co[index][0] + (*disp)*offset[0];
- val[1] = ss->layer_co[index][1] + (*disp)*offset[1];
- val[2] = ss->layer_co[index][2] + (*disp)*offset[2];
+ add_v3_v3(val, ss->layer_co[index]);
}
else {
- val[0] = origco[vd.i][0] + (*disp)*offset[0];
- val[1] = origco[vd.i][1] + (*disp)*offset[1];
- val[2] = origco[vd.i][2] + (*disp)*offset[2];
+ add_v3_v3(val, origco[vd.i]);
}
sculpt_clip(sd, ss, vd.co, val);
- if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
-
- BLI_pbvh_node_mark_update(nodes[n]);
}
}
@@ -1186,206 +1632,721 @@ static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in
float bstrength= ss->cache->bstrength;
int n;
- #pragma omp parallel for private(n) schedule(static)
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
-
- sculpt_undo_push_node(ss, nodes[n]);
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
if(sculpt_brush_test(&test, vd.co)) {
- float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
- float add[3];
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
+ float val[3];
- if(vd.fno) copy_v3_v3(add, vd.fno);
- else normal_short_to_float_v3(add, vd.no);
-
- mul_v3_fl(add, fade * ss->cache->radius);
- add[0]*= ss->cache->scale[0];
- add[1]*= ss->cache->scale[1];
- add[2]*= ss->cache->scale[2];
- add_v3_v3v3(add, add, vd.co);
+ if(vd.fno) copy_v3_v3(val, vd.fno);
+ else normal_short_to_float_v3(val, vd.no);
- sculpt_clip(sd, ss, vd.co, add);
- if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ mul_v3_fl(val, fade * ss->cache->radius);
+ mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BLI_pbvh_vertex_iter_end;
-
- BLI_pbvh_node_mark_update(nodes[n]);
}
}
-static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float co[3])
+static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float fc[3])
{
- float outer_dist[FLATTEN_SAMPLE_SIZE];
- float outer_co[FLATTEN_SAMPLE_SIZE][3];
- int i, n;
-
- for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) {
- zero_v3(outer_co[i]);
- outer_dist[i]= -1.0f;
+ int n;
+
+ float count = 0;
+
+ zero_v3(fc);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n=0; n<totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ SculptUndoNode *unode;
+ float private_fc[3] = {0.0f, 0.0f, 0.0f};
+ int private_count = 0;
+
+ unode = sculpt_undo_push_node(ss, nodes[n]);
+ sculpt_brush_test_init(ss, &test);
+
+ if(ss->cache->original) {
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+ add_v3_v3(private_fc, vd.co);
+ private_count++;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+ else {
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test_fast(&test, vd.co)) {
+ add_v3_v3(private_fc, vd.co);
+ private_count++;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+
+ #pragma omp critical
+ {
+ add_v3_v3(fc, private_fc);
+ count += private_count;
+ }
}
-
- #pragma omp parallel for private(n) schedule(static)
+
+ mul_v3_fl(fc, 1.0f / count);
+}
+
+/* this calculates flatten center and area normal together,
+amortizing the memory bandwidth and loop overhead to calculate both at the same time */
+static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3])
+{
+ int n;
+
+ // an
+ float out_flip[3] = {0.0f, 0.0f, 0.0f};
+
+ // fc
+ float count = 0;
+
+ // an
+ zero_v3(an);
+
+ // fc
+ zero_v3(fc);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
- int j;
-
- sculpt_undo_push_node(ss, nodes[n]);
+ SculptUndoNode *unode;
+ float private_an[3] = {0.0f, 0.0f, 0.0f};
+ float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
+ float private_fc[3] = {0.0f, 0.0f, 0.0f};
+ int private_count = 0;
+
+ unode = sculpt_undo_push_node(ss, nodes[n]);
sculpt_brush_test_init(ss, &test);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test(&test, vd.co)) {
- for(j = 0; j < FLATTEN_SAMPLE_SIZE; ++j) {
- if(test.dist > outer_dist[j]) {
- copy_v3_v3(outer_co[j], vd.co);
- outer_dist[j] = test.dist;
- break;
+ if(ss->cache->original) {
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+ // an
+ float fno[3];
+
+ normal_short_to_float_v3(fno, unode->no[vd.i]);
+ add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
+
+ // fc
+ add_v3_v3(private_fc, vd.co);
+ private_count++;
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+ else {
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(sculpt_brush_test_fast(&test, vd.co)) {
+ // an
+ if(vd.no) {
+ float fno[3];
+
+ normal_short_to_float_v3(fno, vd.no);
+ add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
}
+ else {
+ add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno);
+ }
+
+ // fc
+ add_v3_v3(private_fc, vd.co);
+ private_count++;
}
}
+ BLI_pbvh_vertex_iter_end;
}
- BLI_pbvh_vertex_iter_end;
- BLI_pbvh_node_mark_update(nodes[n]);
+ #pragma omp critical
+ {
+ // an
+ add_v3_v3(an, private_an);
+ add_v3_v3(out_flip, private_out_flip);
+
+ // fc
+ add_v3_v3(fc, private_fc);
+ count += private_count;
+ }
+ }
+
+ // an
+ if (is_zero_v3(an))
+ copy_v3_v3(an, out_flip);
+
+ normalize_v3(an);
+
+ // fc
+ if (count != 0) {
+ mul_v3_fl(fc, 1.0f / count);
+ }
+ else {
+ zero_v3(fc);
+ }
+}
+
+static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3])
+{
+ Brush *brush = paint_brush(&sd->paint);
+
+ if (ss->cache->mirror_symmetry_pass == 0 &&
+ ss->cache->radial_symmetry_pass == 0 &&
+ (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
+ {
+ switch (brush->sculpt_plane) {
+ case SCULPT_DISP_DIR_VIEW:
+ viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an);
+ break;
+
+ case SCULPT_DISP_DIR_X:
+ an[1] = 0.0;
+ an[2] = 0.0;
+ an[0] = 1.0;
+ break;
+
+ case SCULPT_DISP_DIR_Y:
+ an[0] = 0.0;
+ an[2] = 0.0;
+ an[1] = 1.0;
+ break;
+
+ case SCULPT_DISP_DIR_Z:
+ an[0] = 0.0;
+ an[1] = 0.0;
+ an[2] = 1.0;
+ break;
+
+ case SCULPT_DISP_DIR_AREA:
+ calc_area_normal_and_flatten_center(sd, ss, nodes, totnode, an, fc);
+
+ default:
+ break;
+ }
+
+ // fc
+ /* flatten center has not been calculated yet if we are not using the area normal */
+ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA)
+ calc_flatten_center(sd, ss, nodes, totnode, fc);
+
+ // an
+ copy_v3_v3(ss->cache->last_area_normal, an);
+
+ // fc
+ copy_v3_v3(ss->cache->last_center, fc);
+ }
+ else {
+ // an
+ copy_v3_v3(an, ss->cache->last_area_normal);
+
+ // fc
+ copy_v3_v3(fc, ss->cache->last_center);
+
+ // an
+ flip_coord(an, an, ss->cache->mirror_symmetry_pass);
+
+ // fc
+ flip_coord(fc, fc, ss->cache->mirror_symmetry_pass);
+
+ // an
+ mul_m4_v3(ss->cache->symm_rot_mat, an);
+
+ // fc
+ mul_m4_v3(ss->cache->symm_rot_mat, fc);
}
-
- co[0] = co[1] = co[2] = 0.0f;
- for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i)
- if(outer_dist[i] >= 0.0f)
- add_v3_v3v3(co, co, outer_co[i]);
- mul_v3_fl(co, 1.0f / FLATTEN_SAMPLE_SIZE);
}
/* Projects a point onto a plane along the plane's normal */
static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3])
{
- float p1[3], sub1[3], sub2[3];
+ sub_v3_v3v3(intr, co, plane_center);
+ mul_v3_v3fl(intr, plane_normal, dot_v3v3(plane_normal, intr));
+ sub_v3_v3v3(intr, co, intr);
+}
+
+static int plane_trim(StrokeCache *cache, Brush *brush, float val[3])
+{
+ return !(brush->flag & BRUSH_PLANE_TRIM) || (dot_v3v3(val, val) <= cache->radius_squared*cache->plane_trim_squared);
+}
+
+static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip)
+{
+ float delta[3];
+ float d;
+
+ sub_v3_v3v3(delta, co, plane_center);
+ d = dot_v3v3(plane_normal, delta);
+
+ if (flip) d = -d;
+
+ return d <= 0.0f;
+}
+
+static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3])
+{
+ float delta[3];
- /* Find the intersection between squash-plane and vertex (along the area normal) */
- sub_v3_v3v3(p1, co, plane_normal);
- sub_v3_v3v3(sub1, plane_center, p1);
- sub_v3_v3v3(sub2, co, p1);
- sub_v3_v3v3(intr, co, p1);
- mul_v3_fl(intr, dot_v3v3(plane_normal, sub1) / dot_v3v3(plane_normal, sub2));
- add_v3_v3v3(intr, intr, p1);
+ sub_v3_v3v3(delta, co, plane_center);
+ return dot_v3v3(plane_normal, delta) <= 0.0f;
}
-static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3], int flip)
+static float get_offset(Sculpt *sd, SculptSession *ss)
{
- float delta[3];
- float d;
+ Brush* brush = paint_brush(&sd->paint);
- sub_v3_v3v3(delta, co, plane_center);
- d = dot_v3v3(plane_normal, delta);
+ float rv = brush->plane_offset;
- if(flip)
- d = -d;
+ if (brush->flag & BRUSH_OFFSET_PRESSURE) {
+ rv *= ss->cache->pressure;
+ }
- return d <= 0.0f;
+ return rv;
}
-static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, int clay)
+static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
{
- /* area_normal and cntr define the plane towards which vertices are squashed */
Brush *brush = paint_brush(&sd->paint);
- float bstrength= ss->cache->bstrength;
- float area_normal[3];
- float cntr[3], cntr2[3] = {0}, bstr = 0;
- int n, flip = 0;
- calc_area_normal(sd, ss, area_normal, nodes, totnode);
- calc_flatten_center(sd, ss, nodes, totnode, cntr);
+ float bstrength = ss->cache->bstrength;
+ const float radius = ss->cache->radius;
+
+ float an[3];
+ float fc[3];
+
+ float offset = get_offset(sd, ss);
+
+ float displace;
+
+ int n;
+
+ float temp[3];
+
+ calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+ displace = radius*offset;
+
+ mul_v3_v3v3(temp, an, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(fc, temp);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for(n = 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test_sq(&test, vd.co)) {
+ float intr[3];
+ float val[3];
+
+ point_plane_project(intr, vd.co, an, fc);
+
+ sub_v3_v3v3(val, intr, vd.co);
+
+ if (plane_trim(ss->cache, brush, val)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
- if(clay) {
- bstr= brush_strength(sd, ss->cache);
- /* Limit clay application to here */
- cntr2[0]=cntr[0]+area_normal[0]*bstr*ss->cache->scale[0];
- cntr2[1]=cntr[1]+area_normal[1]*bstr*ss->cache->scale[1];
- cntr2[2]=cntr[2]+area_normal[2]*bstr*ss->cache->scale[2];
- flip = bstr < 0;
+ mul_v3_v3fl(proxy[vd.i], val, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
}
+}
- //#pragma omp parallel for private(n) schedule(static)
- for(n=0; n<totnode; n++) {
+static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
+
+ float bstrength = ss->cache->bstrength;
+ float radius = ss->cache->radius;
+ float offset = get_offset(sd, ss);
+
+ float displace;
+
+ float an[3]; // area normal
+ float fc[3]; // flatten center
+
+ int n;
+
+ float temp[3];
+ //float p[3];
+
+ int flip;
+
+ calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+ flip = bstrength < 0;
+
+ if (flip) {
+ bstrength = -bstrength;
+ radius = -radius;
+ }
+
+ displace = radius * (0.25f+offset);
+
+ mul_v3_v3v3(temp, an, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(fc, temp);
+
+ //add_v3_v3v3(p, ss->cache->location, an);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
SculptBrushTest test;
-
- sculpt_undo_push_node(ss, nodes[n]);
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
sculpt_brush_test_init(ss, &test);
BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test(&test, vd.co)) {
- float intr[3], val[3];
-
- if(!clay || plane_point_side(vd.co, area_normal, cntr2, flip)) {
- const float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
+ if (sculpt_brush_test_sq(&test, vd.co)) {
+ if (plane_point_side_flip(vd.co, an, fc, flip)) {
+ //if (sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, p)) {
+ float intr[3];
+ float val[3];
- /* Find the intersection between squash-plane and vertex (along the area normal) */
- point_plane_project(intr, vd.co, area_normal, cntr);
+ point_plane_project(intr, vd.co, an, fc);
sub_v3_v3v3(val, intr, vd.co);
- if(clay) {
- if(bstr > FLT_EPSILON)
- mul_v3_fl(val, fade / bstr);
- else
- mul_v3_fl(val, fade);
- /* Clay displacement */
- val[0]+=area_normal[0] * ss->cache->scale[0]*fade;
- val[1]+=area_normal[1] * ss->cache->scale[1]*fade;
- val[2]+=area_normal[2] * ss->cache->scale[2]*fade;
+ if (plane_trim(ss->cache, brush, val)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
+
+ mul_v3_v3fl(proxy[vd.i], val, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
- else
- mul_v3_fl(val, fabs(fade));
+ }
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+}
- add_v3_v3v3(val, val, vd.co);
+static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
- sculpt_clip(sd, ss, vd.co, val);
- if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ float bstrength = ss->cache->bstrength;
+ float radius = ss->cache->radius;
+ float offset = get_offset(sd, ss);
+
+ float displace;
+
+ float sn[3]; // sculpt normal
+ float an[3]; // area normal
+ float fc[3]; // flatten center
+
+ int n;
+
+ float temp[3];
+ float mat[4][4];
+ float scale[4][4];
+ float tmat[4][4];
+
+ int flip;
+
+ calc_sculpt_plane(sd, ss, nodes, totnode, sn, fc);
+
+ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL))
+ calc_area_normal(sd, ss, an, nodes, totnode);
+ else
+ copy_v3_v3(an, sn);
+
+ if (ss->cache->first_time)
+ return; // delay the first daub because grab delta is not setup
+
+ flip = bstrength < 0;
+
+ if (flip) {
+ bstrength = -bstrength;
+ radius = -radius;
+ }
+
+ displace = radius * (0.25f+offset);
+
+ mul_v3_v3v3(temp, sn, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(fc, temp);
+
+ cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry); mat[0][3] = 0;
+ cross_v3_v3v3(mat[1], an, mat[0]); mat[1][3] = 0;
+ copy_v3_v3(mat[2], an); mat[2][3] = 0;
+ copy_v3_v3(mat[3], ss->cache->location); mat[3][3] = 1;
+ normalize_m4(mat);
+ scale_m4_fl(scale, ss->cache->radius);
+ mul_m4_m4m4(tmat, scale, mat);
+ invert_m4_m4(mat, tmat);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for (n = 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test_cube(&test, vd.co, mat)) {
+ if (plane_point_side_flip(vd.co, sn, fc, flip)) {
+ float intr[3];
+ float val[3];
+
+ point_plane_project(intr, vd.co, sn, fc);
+
+ sub_v3_v3v3(val, intr, vd.co);
+
+ if (plane_trim(ss->cache, brush, val)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, ss->cache->radius*test.dist)*frontface(brush, an, vd.no, vd.fno);
+
+ mul_v3_v3fl(proxy[vd.i], val, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
}
}
}
BLI_pbvh_vertex_iter_end;
+ }
+}
+
+static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = paint_brush(&sd->paint);
+
+ float bstrength = ss->cache->bstrength;
+ const float radius = ss->cache->radius;
+
+ float an[3];
+ float fc[3];
+ float offset = get_offset(sd, ss);
+
+ float displace;
+
+ int n;
+
+ float temp[3];
+
+ calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+ displace = radius*offset;
+
+ mul_v3_v3v3(temp, an, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(fc, temp);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for (n = 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
- BLI_pbvh_node_mark_update(nodes[n]);
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test_sq(&test, vd.co)) {
+ if (plane_point_side(vd.co, an, fc)) {
+ float intr[3];
+ float val[3];
+
+ point_plane_project(intr, vd.co, an, fc);
+
+ sub_v3_v3v3(val, intr, vd.co);
+
+ if (plane_trim(ss->cache, brush, val)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
+
+ mul_v3_v3fl(proxy[vd.i], val, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
}
}
-static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
+static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
{
- SculptSearchSphereData data;
Brush *brush = paint_brush(&sd->paint);
- PBVHNode **nodes= NULL;
- int totnode;
- data.ss = ss;
- data.sd = sd;
- data.radius_squared = ss->cache->radius * ss->cache->radius;
+ float bstrength = ss->cache->bstrength;
+ const float radius = ss->cache->radius;
- /* Build a list of all nodes that are potentially within the brush's
- area of influence */
- if(brush->sculpt_tool == SCULPT_TOOL_GRAB) {
- data.original= 1;
- BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data,
- &nodes, &totnode);
+ float an[3];
+ float fc[3];
+ float offset = get_offset(sd, ss);
- if(cache->first_time)
- copy_v3_v3(ss->cache->grab_active_location[ss->cache->symmetry], ss->cache->location);
- else
- copy_v3_v3(ss->cache->location, ss->cache->grab_active_location[ss->cache->symmetry]);
+ float displace;
+
+ int n;
+
+ float temp[3];
+
+ calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+ displace = -radius*offset;
+
+ mul_v3_v3v3(temp, an, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(fc, temp);
+
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for (n = 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float (*proxy)[3];
+
+ proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+ sculpt_brush_test_init(ss, &test);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test_sq(&test, vd.co)) {
+ if (!plane_point_side(vd.co, an, fc)) {
+ float intr[3];
+ float val[3];
+
+ point_plane_project(intr, vd.co, an, fc);
+
+ sub_v3_v3v3(val, intr, vd.co);
+
+ if (plane_trim(ss->cache, brush, val)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
+
+ mul_v3_v3fl(proxy[vd.i], val, fade);
+
+ if(vd.mvert)
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
}
- else {
- BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data,
- &nodes, &totnode);
+}
+
+void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
+{
+ Mesh *me= (Mesh*)ob->data;
+ float (*ofs)[3]= NULL;
+ int a, is_basis= 0;
+ KeyBlock *currkey;
+
+ /* for relative keys editing of base should update other keys */
+ if (me->key->type == KEY_RELATIVE)
+ for (currkey = me->key->block.first; currkey; currkey= currkey->next)
+ if(ob->shapenr-1 == currkey->relative) {
+ is_basis= 1;
+ break;
+ }
+
+ if (is_basis) {
+ ofs= key_to_vertcos(ob, kb);
+
+ /* calculate key coord offsets (from previous location) */
+ for (a= 0; a < me->totvert; a++) {
+ VECSUB(ofs[a], vertCos[a], ofs[a]);
+ }
+
+ /* apply offsets on other keys */
+ currkey = me->key->block.first;
+ while (currkey) {
+ int apply_offset = ((currkey != kb) && (ob->shapenr-1 == currkey->relative));
+
+ if (apply_offset)
+ offset_to_key(ob, currkey, ofs);
+
+ currkey= currkey->next;
+ }
+
+ MEM_freeN(ofs);
+ }
+
+ /* modifying of basis key should update mesh */
+ if (kb == me->key->refkey) {
+ MVert *mvert= me->mvert;
+
+ for (a= 0; a < me->totvert; a++, mvert++)
+ VECCOPY(mvert->co, vertCos[a]);
+
+ mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
+ }
+
+ /* apply new coords on active key block */
+ vertcos_to_key(ob, kb, vertCos);
+}
+
+/* copy the modified vertices from bvh to the active key */
+static void sculpt_update_keyblock(SculptSession *ss)
+{
+ float (*vertCos)[3]= BLI_pbvh_get_vertCos(ss->pbvh);
+
+ if (vertCos) {
+ sculpt_vertcos_to_key(ss->ob, ss->kb, vertCos);
+ MEM_freeN(vertCos);
}
+}
+
+static void do_brush_action(Sculpt *sd, SculptSession *ss, Brush *brush)
+{
+ SculptSearchSphereData data;
+ PBVHNode **nodes = NULL;
+ int n, totnode;
+
+ /* Build a list of all nodes that are potentially within the brush's area of influence */
+ data.ss = ss;
+ data.sd = sd;
+ data.radius_squared = ss->cache->radius_squared;
+ data.original = ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER);
+ BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
/* Only act if some verts are inside the brush area */
- if(totnode) {
+ if (totnode) {
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for (n= 0; n < totnode; n++) {
+ sculpt_undo_push_node(ss, nodes[n]);
+ BLI_pbvh_node_mark_update(nodes[n]);
+ }
+
/* Apply one type of brush action */
switch(brush->sculpt_tool){
case SCULPT_TOOL_DRAW:
@@ -1394,6 +2355,12 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
case SCULPT_TOOL_SMOOTH:
do_smooth_brush(sd, ss, nodes, totnode);
break;
+ case SCULPT_TOOL_CREASE:
+ do_crease_brush(sd, ss, nodes, totnode);
+ break;
+ case SCULPT_TOOL_BLOB:
+ do_crease_brush(sd, ss, nodes, totnode);
+ break;
case SCULPT_TOOL_PINCH:
do_pinch_brush(sd, ss, nodes, totnode);
break;
@@ -1403,60 +2370,246 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
case SCULPT_TOOL_GRAB:
do_grab_brush(sd, ss, nodes, totnode);
break;
+ case SCULPT_TOOL_ROTATE:
+ do_rotate_brush(sd, ss, nodes, totnode);
+ break;
+ case SCULPT_TOOL_SNAKE_HOOK:
+ do_snake_hook_brush(sd, ss, nodes, totnode);
+ break;
+ case SCULPT_TOOL_NUDGE:
+ do_nudge_brush(sd, ss, nodes, totnode);
+ break;
+ case SCULPT_TOOL_THUMB:
+ do_thumb_brush(sd, ss, nodes, totnode);
+ break;
case SCULPT_TOOL_LAYER:
do_layer_brush(sd, ss, nodes, totnode);
break;
case SCULPT_TOOL_FLATTEN:
- do_flatten_clay_brush(sd, ss, nodes, totnode, 0);
+ do_flatten_brush(sd, ss, nodes, totnode);
break;
case SCULPT_TOOL_CLAY:
- do_flatten_clay_brush(sd, ss, nodes, totnode, 1);
+ do_clay_brush(sd, ss, nodes, totnode);
+ break;
+ case SCULPT_TOOL_CLAY_TUBES:
+ do_clay_tubes_brush(sd, ss, nodes, totnode);
+ break;
+ case SCULPT_TOOL_FILL:
+ do_fill_brush(sd, ss, nodes, totnode);
+ break;
+ case SCULPT_TOOL_SCRAPE:
+ do_scrape_brush(sd, ss, nodes, totnode);
break;
}
-
+
+ if (brush->sculpt_tool != SCULPT_TOOL_SMOOTH && brush->autosmooth_factor > 0) {
+ if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) {
+ smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*(1-ss->cache->pressure));
+ }
+ else {
+ smooth(sd, ss, nodes, totnode, brush->autosmooth_factor);
+ }
+ }
+
/* copy the modified vertices from mesh to the active key */
- if(ss->kb) mesh_to_key(ss->ob->data, ss->kb);
-
- if(nodes)
- MEM_freeN(nodes);
- }
+ if(ss->kb)
+ mesh_to_key(ss->ob->data, ss->kb);
+
+ /* optimization: we could avoid copying new coords to keyblock at each */
+ /* stroke step if there are no modifiers due to pbvh is used for displaying */
+ /* so to increase speed we'll copy new coords to keyblock when stroke is done */
+ if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ss);
+
+ MEM_freeN(nodes);
+ }
}
+static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss)
+{
+ Brush *brush= paint_brush(&sd->paint);
+ PBVHNode** nodes;
+ int totnode;
+ int n;
+
+ BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
+
+ switch (brush->sculpt_tool) {
+ case SCULPT_TOOL_GRAB:
+ case SCULPT_TOOL_ROTATE:
+ case SCULPT_TOOL_THUMB:
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for (n= 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ PBVHProxyNode* proxies;
+ int proxy_count;
+ float (*origco)[3];
+
+ origco= sculpt_undo_push_node(ss, nodes[n])->co;
+
+ BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ float val[3];
+ int p;
+
+ copy_v3_v3(val, origco[vd.i]);
+
+ for (p= 0; p < proxy_count; p++)
+ add_v3_v3(val, proxies[p].co[vd.i]);
+
+ sculpt_clip(sd, ss, vd.co, val);
+ }
+ BLI_pbvh_vertex_iter_end;
+
+ BLI_pbvh_node_free_proxies(nodes[n]);
+ }
+
+ break;
+
+ case SCULPT_TOOL_DRAW:
+ case SCULPT_TOOL_CLAY:
+ case SCULPT_TOOL_CLAY_TUBES:
+ case SCULPT_TOOL_CREASE:
+ case SCULPT_TOOL_BLOB:
+ case SCULPT_TOOL_FILL:
+ case SCULPT_TOOL_FLATTEN:
+ case SCULPT_TOOL_INFLATE:
+ case SCULPT_TOOL_NUDGE:
+ case SCULPT_TOOL_PINCH:
+ case SCULPT_TOOL_SCRAPE:
+ case SCULPT_TOOL_SNAKE_HOOK:
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ for (n= 0; n < totnode; n++) {
+ PBVHVertexIter vd;
+ PBVHProxyNode* proxies;
+ int proxy_count;
+
+ BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count);
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ float val[3];
+ int p;
+
+ copy_v3_v3(val, vd.co);
+
+ for (p= 0; p < proxy_count; p++)
+ add_v3_v3(val, proxies[p].co[vd.i]);
+
+ sculpt_clip(sd, ss, vd.co, val);
+ }
+ BLI_pbvh_vertex_iter_end;
+
+ BLI_pbvh_node_free_proxies(nodes[n]);
+
+ }
+
+ break;
+
+ case SCULPT_TOOL_SMOOTH:
+ case SCULPT_TOOL_LAYER:
+ default:
+ break;
+ }
+
+ if (nodes)
+ MEM_freeN(nodes);
+}
+
+//static int max_overlap_count(Sculpt *sd)
+//{
+// int count[3];
+// int i, j;
+//
+// for (i= 0; i < 3; i++) {
+// count[i] = sd->radial_symm[i];
+//
+// for (j= 0; j < 3; j++) {
+// if (i != j && sd->flags & (SCULPT_SYMM_X<<i))
+// count[i] *= 2;
+// }
+// }
+//
+// return MAX3(count[0], count[1], count[2]);
+//}
+
/* Flip all the editdata across the axis/axes specified by symm. Used to
calculate multiple modifications to the mesh when symmetry is enabled. */
-static void calc_brushdata_symm(StrokeCache *cache, const char symm)
+static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, const char axis, const float angle, const float feather)
{
flip_coord(cache->location, cache->true_location, symm);
- flip_coord(cache->view_normal_symmetry, cache->view_normal, symm);
flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm);
- cache->symmetry= symm;
+ flip_coord(cache->view_normal, cache->true_view_normal, symm);
+
+ // XXX This reduces the length of the grab delta if it approaches the line of symmetry
+ // XXX However, a different approach appears to be needed
+ //if (sd->flags & SCULPT_SYMMETRY_FEATHER) {
+ // float frac = 1.0f/max_overlap_count(sd);
+ // float reduce = (feather-frac)/(1-frac);
+
+ // printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce);
+
+ // if (frac < 1)
+ // mul_v3_fl(cache->grab_delta_symmetry, reduce);
+ //}
+
+ unit_m4(cache->symm_rot_mat);
+ unit_m4(cache->symm_rot_mat_inv);
+ rotate_m4(cache->symm_rot_mat, axis, angle);
+ rotate_m4(cache->symm_rot_mat_inv, axis, -angle);
+
+ mul_m4_v3(cache->symm_rot_mat, cache->location);
+ mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry);
+}
+
+static void do_radial_symmetry(Sculpt *sd, SculptSession *ss, Brush *brush, const char symm, const int axis, const float feather)
+{
+ int i;
+
+ for(i = 1; i < sd->radial_symm[axis-'X']; ++i) {
+ const float angle = 2*M_PI*i/sd->radial_symm[axis-'X'];
+ ss->cache->radial_symmetry_pass= i;
+ calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather);
+ do_brush_action(sd, ss, brush);
+ }
}
static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss)
{
+ Brush *brush = paint_brush(&sd->paint);
StrokeCache *cache = ss->cache;
const char symm = sd->flags & 7;
int i;
- copy_v3_v3(cache->location, cache->true_location);
- copy_v3_v3(cache->grab_delta_symmetry, cache->grab_delta);
- cache->symmetry = 0;
- cache->bstrength = brush_strength(sd, cache);
- do_brush_action(sd, ss, cache);
+ float feather = calc_symmetry_feather(sd, ss->cache);
+
+ cache->bstrength= brush_strength(sd, cache, feather);
- for(i = 1; i <= symm; ++i) {
- if(symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) {
- calc_brushdata_symm(cache, i);
- do_brush_action(sd, ss, cache);
+ cache->symmetry= symm;
+
+ /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
+ for(i = 0; i <= symm; ++i) {
+ if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
+ cache->mirror_symmetry_pass= i;
+ cache->radial_symmetry_pass= 0;
+
+ calc_brushdata_symm(sd, cache, i, 0, 0, feather);
+ do_brush_action(sd, ss, brush);
+
+ do_radial_symmetry(sd, ss, brush, i, 'X', feather);
+ do_radial_symmetry(sd, ss, brush, i, 'Y', feather);
+ do_radial_symmetry(sd, ss, brush, i, 'Z', feather);
}
}
- cache->first_time = 0;
+ sculpt_combine_proxies(sd, ss);
+
+ cache->first_time= 0;
}
static void sculpt_update_tex(Sculpt *sd, SculptSession *ss)
{
Brush *brush = paint_brush(&sd->paint);
+ const int radius= brush_size(brush);
if(ss->texcache) {
MEM_freeN(ss->texcache);
@@ -1464,72 +2617,28 @@ static void sculpt_update_tex(Sculpt *sd, SculptSession *ss)
}
/* Need to allocate a bigger buffer for bigger brush size */
- ss->texcache_side = brush->size * 2;
+ ss->texcache_side = 2*radius;
if(!ss->texcache || ss->texcache_side > ss->texcache_actual) {
- ss->texcache = brush_gen_texture_cache(brush, brush->size);
+ ss->texcache = brush_gen_texture_cache(brush, radius);
ss->texcache_actual = ss->texcache_side;
}
}
-/* Sculpt mode handles multires differently from regular meshes, but only if
- it's the last modifier on the stack and it is not on the first level */
-struct MultiresModifierData *sculpt_multires_active(Object *ob)
-{
- ModifierData *md, *nmd;
-
- for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
- if(md->type == eModifierType_Multires) {
- MultiresModifierData *mmd= (MultiresModifierData*)md;
-
- /* Check if any of the modifiers after multires are active
- * if not it can use the multires struct */
- for (nmd= md->next; nmd; nmd= nmd->next)
- if(nmd->mode & eModifierMode_Realtime)
- break;
-
- if(!nmd && mmd->sculptlvl > 0)
- return mmd;
- }
- }
-
- return NULL;
-}
-
-void sculpt_key_to_mesh(KeyBlock *kb, Object *ob)
-{
- Mesh *me= ob->data;
-
- key_to_mesh(kb, me);
- mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
-}
-
-void sculpt_mesh_to_key(Object *ob, KeyBlock *kb)
-{
- Mesh *me= ob->data;
-
- mesh_to_key(me, kb);
-}
-
void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap)
{
- DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
+ DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
SculptSession *ss = ob->sculpt;
-
+ MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
+
ss->ob= ob;
- if((ob->shapeflag & OB_SHAPE_LOCK) && !sculpt_multires_active(ob)) {
- ss->kb= ob_get_keyblock(ob);
- ss->refkb= ob_get_reference_keyblock(ob);
- }
- else {
- ss->kb= NULL;
- ss->refkb= NULL;
- }
+ ss->modifiers_active= sculpt_modifiers_active(scene, ob);
- /* need to make PBVH with shape key coordinates */
- if(ss->kb) sculpt_key_to_mesh(ss->kb, ss->ob);
+ if((ob->shapeflag & OB_SHAPE_LOCK) && !mmd) ss->kb= ob_get_keyblock(ob);
+ else ss->kb= NULL;
- if((ss->multires = sculpt_multires_active(ob))) {
+ if(mmd) {
+ ss->multires = mmd;
ss->totvert = dm->getNumVerts(dm);
ss->totface = dm->getNumFaces(dm);
ss->mvert= NULL;
@@ -1543,10 +2652,22 @@ void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap)
ss->mvert = me->mvert;
ss->mface = me->mface;
ss->face_normals = NULL;
+ ss->multires = NULL;
}
ss->pbvh = dm->getPBVH(ob, dm);
ss->fmap = (need_fmap && dm->getFaceMap)? dm->getFaceMap(ob, dm): NULL;
+
+ /* if pbvh is deformed, key block is already applied to it */
+ if (ss->kb && !BLI_pbvh_isDeformed(ss->pbvh)) {
+ float (*vertCos)[3]= key_to_vertcos(ob, ss->kb);
+
+ if (vertCos) {
+ /* apply shape keys coordinates to PBVH */
+ BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
+ MEM_freeN(vertCos);
+ }
+ }
}
static int sculpt_mode_poll(bContext *C)
@@ -1569,16 +2690,32 @@ static char *sculpt_tool_name(Sculpt *sd)
return "Draw Brush"; break;
case SCULPT_TOOL_SMOOTH:
return "Smooth Brush"; break;
+ case SCULPT_TOOL_CREASE:
+ return "Crease Brush"; break;
+ case SCULPT_TOOL_BLOB:
+ return "Blob Brush"; break;
case SCULPT_TOOL_PINCH:
return "Pinch Brush"; break;
case SCULPT_TOOL_INFLATE:
return "Inflate Brush"; break;
case SCULPT_TOOL_GRAB:
return "Grab Brush"; break;
+ case SCULPT_TOOL_NUDGE:
+ return "Nudge Brush"; break;
+ case SCULPT_TOOL_THUMB:
+ return "Thumb Brush"; break;
case SCULPT_TOOL_LAYER:
return "Layer Brush"; break;
case SCULPT_TOOL_FLATTEN:
- return "Flatten Brush"; break;
+ return "Flatten Brush"; break;
+ case SCULPT_TOOL_CLAY:
+ return "Clay Brush"; break;
+ case SCULPT_TOOL_CLAY_TUBES:
+ return "Clay Tubes Brush"; break;
+ case SCULPT_TOOL_FILL:
+ return "Fill Brush"; break;
+ case SCULPT_TOOL_SCRAPE:
+ return "Scrape Brush"; break;
default:
return "Sculpting"; break;
}
@@ -1589,10 +2726,24 @@ static int sculpt_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *ev
{
Paint *p = paint_get_active(CTX_data_scene(C));
Brush *brush = paint_brush(p);
+ float col[4], tex_col[4];
WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor);
p->paint_cursor = NULL;
brush_radial_control_invoke(op, brush, 1);
+
+ if((brush->flag & BRUSH_DIR_IN) && ELEM4(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH))
+ copy_v3_v3(col, brush->sub_col);
+ else
+ copy_v3_v3(col, brush->add_col);
+ col[3]= 0.5f;
+
+ copy_v3_v3(tex_col, U.sculpt_paint_overlay_col);
+ tex_col[3]= (brush->texture_overlay_alpha / 100.0f);
+
+ RNA_float_set_array(op->ptr, "color", col);
+ RNA_float_set_array(op->ptr, "texture_color", tex_col);
+
return WM_radial_control_invoke(C, op, event);
}
@@ -1657,19 +2808,69 @@ static void sculpt_cache_free(StrokeCache *cache)
}
/* Initialize the stroke cache invariants from operator properties */
-static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bContext *C, wmOperator *op)
+static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSession *ss, wmOperator *op, wmEvent *event)
{
StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
Brush *brush = paint_brush(&sd->paint);
ViewContext *vc = paint_stroke_view_context(op->customdata);
+ Object *ob= CTX_data_active_object(C);
+ ModifierData *md;
int i;
+ int mode;
ss->cache = cache;
- RNA_float_get_array(op->ptr, "scale", cache->scale);
- cache->flag = RNA_int_get(op->ptr, "flag");
- RNA_float_get_array(op->ptr, "clip_tolerance", cache->clip_tolerance);
- RNA_float_get_array(op->ptr, "initial_mouse", cache->initial_mouse);
+ /* 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];
+
+ ss->cache->plane_trim_squared = brush->plane_trim * brush->plane_trim;
+
+ /* Initialize mirror modifier clipping */
+
+ ss->cache->flag = 0;
+
+ for(md= ob->modifiers.first; md; md= md->next) {
+ if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
+ const MirrorModifierData *mmd = (MirrorModifierData*) md;
+
+ /* Mark each axis that needs clipping along with its tolerance */
+ if(mmd->flag & MOD_MIR_CLIPPING) {
+ ss->cache->flag |= CLIP_X << mmd->axis;
+ if(mmd->tolerance > ss->cache->clip_tolerance[mmd->axis])
+ ss->cache->clip_tolerance[mmd->axis] = mmd->tolerance;
+ }
+ }
+ }
+
+ /* Initial mouse location */
+ if (event) {
+ ss->cache->initial_mouse[0] = event->x;
+ ss->cache->initial_mouse[1] = event->y;
+ }
+ else {
+ ss->cache->initial_mouse[0] = 0;
+ ss->cache->initial_mouse[1] = 0;
+ }
+
+ mode = RNA_int_get(op->ptr, "mode");
+ cache->invert = mode == WM_BRUSHSTROKE_INVERT;
+ cache->alt_smooth = mode == WM_BRUSHSTROKE_SMOOTH;
+
+ /* Alt-Smooth */
+ if (ss->cache->alt_smooth) {
+ Paint *p= &sd->paint;
+ Brush *br;
+
+ BLI_strncpy(cache->saved_active_brush_name, brush->id.name+2, sizeof(cache->saved_active_brush_name));
+
+ br= (Brush *)find_id("BR", "Smooth");
+ if(br) {
+ paint_brush_set(p, br);
+ brush = br;
+ }
+ }
copy_v2_v2(cache->mouse, cache->initial_mouse);
copy_v2_v2(cache->tex_mouse, cache->initial_mouse);
@@ -1677,11 +2878,13 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte
/* Truly temporary data that isn't stored in properties */
cache->vc = vc;
+
cache->brush = brush;
cache->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats");
view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, cache->mats);
+ viewvector(cache->vc->rv3d, cache->vc->rv3d->twmat[3], cache->true_view_normal);
/* Initialize layer brush displacements and persistent coords */
if(brush->sculpt_tool == SCULPT_TOOL_LAYER) {
/* not supported yet for multires */
@@ -1707,103 +2910,233 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte
cache->original = 1;
}
- if(ELEM3(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE))
+ if(ELEM7(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_TUBES))
if(!(brush->flag & BRUSH_ACCUMULATE))
cache->original = 1;
- cache->rotation = 0;
- cache->first_time = 1;
+ cache->special_rotation = (brush->flag & BRUSH_RAKE) ? sd->last_angle : 0;
+ //cache->last_rake[0] = sd->last_x;
+ //cache->last_rake[1] = sd->last_y;
+
+ cache->first_time= 1;
+
+ cache->vertex_rotation= 0;
+}
+
+static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brush)
+{
+ StrokeCache *cache = ss->cache;
+ int tool = brush->sculpt_tool;
+
+ if(ELEM5(tool,
+ SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE,
+ SCULPT_TOOL_CLAY_TUBES, SCULPT_TOOL_SNAKE_HOOK,
+ SCULPT_TOOL_THUMB)) {
+ float grab_location[3], imat[4][4], delta[3];
+
+ if(cache->first_time) {
+ copy_v3_v3(cache->orig_grab_location,
+ cache->true_location);
+ }
+ else if(tool == SCULPT_TOOL_SNAKE_HOOK)
+ add_v3_v3(cache->true_location, cache->grab_delta);
+
+ /* compute 3d coordinate at same z from original location + mouse */
+ initgrabz(cache->vc->rv3d,
+ cache->orig_grab_location[0],
+ cache->orig_grab_location[1],
+ cache->orig_grab_location[2]);
+
+ window_to_3d_delta(cache->vc->ar, grab_location,
+ cache->mouse[0], cache->mouse[1]);
+
+ /* compute delta to move verts by */
+ if(!cache->first_time) {
+ switch(tool) {
+ case SCULPT_TOOL_GRAB:
+ case SCULPT_TOOL_THUMB:
+ sub_v3_v3v3(delta, grab_location, cache->old_grab_location);
+ invert_m4_m4(imat, ss->ob->obmat);
+ mul_mat3_m4_v3(imat, delta);
+ add_v3_v3(cache->grab_delta, delta);
+ break;
+ case SCULPT_TOOL_CLAY_TUBES:
+ case SCULPT_TOOL_NUDGE:
+ sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
+ invert_m4_m4(imat, ss->ob->obmat);
+ mul_mat3_m4_v3(imat, cache->grab_delta);
+ break;
+ case SCULPT_TOOL_SNAKE_HOOK:
+ sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
+ invert_m4_m4(imat, ss->ob->obmat);
+ mul_mat3_m4_v3(imat, cache->grab_delta);
+ break;
+ }
+ }
+ else {
+ zero_v3(cache->grab_delta);
+ }
+
+ copy_v3_v3(cache->old_grab_location, grab_location);
+
+ if(tool == SCULPT_TOOL_GRAB)
+ copy_v3_v3(sd->anchored_location, cache->true_location);
+ else if(tool == SCULPT_TOOL_THUMB)
+ copy_v3_v3(sd->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 */
+ copy_v3_v3(cache->true_location, cache->orig_grab_location);
+
+ sd->draw_anchored = 1;
+ copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse);
+ sd->anchored_size = cache->pixel_radius;
+ }
+ }
}
/* Initialize the stroke cache variants from operator properties */
-static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr)
+static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr)
{
StrokeCache *cache = ss->cache;
Brush *brush = paint_brush(&sd->paint);
-
+
int dx, dy;
- if(!(brush->flag & BRUSH_ANCHORED) || cache->first_time)
+ //RNA_float_get_array(ptr, "location", cache->traced_location);
+
+ if (cache->first_time ||
+ !((brush->flag & BRUSH_ANCHORED)||
+ (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK)||
+ (brush->sculpt_tool == SCULPT_TOOL_ROTATE))
+ )
+ {
RNA_float_get_array(ptr, "location", cache->true_location);
- cache->flip = RNA_boolean_get(ptr, "flip");
+ }
+
+ cache->pen_flip = RNA_boolean_get(ptr, "pen_flip");
RNA_float_get_array(ptr, "mouse", cache->mouse);
+
cache->pressure = RNA_float_get(ptr, "pressure");
-
+
/* Truly temporary data that isn't stored in properties */
+ sd->draw_pressure= 1;
+ sd->pressure_value= cache->pressure;
+
cache->previous_pixel_radius = cache->pixel_radius;
- cache->pixel_radius = brush->size;
+ cache->pixel_radius = brush_size(brush);
+
+ if(cache->first_time) {
+ if (!brush_use_locked_size(brush)) {
+ cache->initial_radius= unproject_brush_radius(ss->ob, cache->vc, cache->true_location, brush_size(brush));
+ brush_set_unprojected_radius(brush, cache->initial_radius);
+ }
+ else {
+ cache->initial_radius= brush_unprojected_radius(brush);
+ }
- if(cache->first_time)
- cache->initial_radius = unproject_brush_radius(ss->ob, cache->vc, cache->true_location, brush->size);
+ if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK))
+ cache->initial_radius *= 2.0f;
+ }
- if(brush->flag & BRUSH_SIZE_PRESSURE && brush->sculpt_tool != SCULPT_TOOL_GRAB) {
+ if(brush_use_size_pressure(brush)) {
cache->pixel_radius *= cache->pressure;
- cache->radius = cache->initial_radius * cache->pressure;
+ cache->radius= cache->initial_radius * cache->pressure;
}
else
- cache->radius = cache->initial_radius;
+ cache->radius= cache->initial_radius;
+
+ cache->radius_squared = cache->radius*cache->radius;
- if(!(brush->flag & BRUSH_ANCHORED))
+ 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_FIXED) &&
+ (brush->flag & BRUSH_RANDOM_ROTATION) &&
+ !(brush->flag & BRUSH_RAKE))
+ {
+ cache->special_rotation = 2*M_PI*BLI_frand();
+ }
+ }
+
if(brush->flag & BRUSH_ANCHORED) {
+ int hit = 0;
+
dx = cache->mouse[0] - cache->initial_mouse[0];
dy = cache->mouse[1] - cache->initial_mouse[1];
- cache->pixel_radius = sqrt(dx*dx + dy*dy);
- cache->radius = unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke),
- cache->true_location, cache->pixel_radius);
- cache->rotation = atan2(dy, dx);
- }
- else if(brush->flag & BRUSH_RAKE) {
- int update;
- dx = cache->last_rake[0] - cache->mouse[0];
- dy = cache->last_rake[1] - cache->mouse[1];
+ sd->anchored_size = cache->pixel_radius = sqrt(dx*dx + dy*dy);
- update = dx*dx + dy*dy > 100;
+ cache->special_rotation = atan2(dx, dy) + M_PI;
- /* To prevent jitter, only update the angle if the mouse has moved over 10 pixels */
- if(update && !cache->first_time)
- cache->rotation = M_PI_2 + atan2(dy, dx);
+ if (brush->flag & BRUSH_EDGE_TO_EDGE) {
+ float halfway[2];
+ float out[3];
- if(update || cache->first_time) {
- cache->last_rake[0] = cache->mouse[0];
- cache->last_rake[1] = cache->mouse[1];
+ halfway[0] = dx*0.5 + cache->initial_mouse[0];
+ halfway[1] = dy*0.5 + cache->initial_mouse[1];
+
+ if (sculpt_stroke_get_location(C, stroke, out, halfway)) {
+ copy_v3_v3(sd->anchored_location, out);
+ copy_v2_v2(sd->anchored_initial_mouse, halfway);
+ copy_v2_v2(cache->tex_mouse, halfway);
+ copy_v3_v3(cache->true_location, sd->anchored_location);
+ sd->anchored_size /= 2.0f;
+ cache->pixel_radius /= 2.0f;
+ hit = 1;
+ }
}
- }
- /* Find the grab delta */
- if(brush->sculpt_tool == SCULPT_TOOL_GRAB) {
- float grab_location[3], imat[4][4];
+ if (!hit)
+ copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse);
- if(cache->first_time)
- copy_v3_v3(cache->orig_grab_location, cache->true_location);
+ cache->radius= unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke), cache->true_location, cache->pixel_radius);
+ cache->radius_squared = cache->radius*cache->radius;
- /* compute 3d coordinate at same z from original location + mouse */
- initgrabz(cache->vc->rv3d, cache->orig_grab_location[0],
- cache->orig_grab_location[1], cache->orig_grab_location[2]);
- window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]);
+ copy_v3_v3(sd->anchored_location, cache->true_location);
- /* compute delta to move verts by */
- if(!cache->first_time) {
- sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
- invert_m4_m4(imat, ss->ob->obmat);
- mul_mat3_m4_v3(imat, cache->grab_delta);
+ sd->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);
- copy_v3_v3(cache->old_grab_location, grab_location);
+ 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];
+ }
+ }
+
+ sculpt_update_brush_delta(sd, ss, brush);
+
+ if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) {
+ dx = cache->mouse[0] - cache->initial_mouse[0];
+ dy = cache->mouse[1] - cache->initial_mouse[1];
+
+ cache->vertex_rotation = -atan2(dx, dy);
- /* location stays the same for finding vertices in brush radius */
- copy_v3_v3(cache->true_location, cache->orig_grab_location);
+ sd->draw_anchored = 1;
+ copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse);
+ copy_v3_v3(sd->anchored_location, cache->true_location);
+ sd->anchored_size = cache->pixel_radius;
}
+
+ sd->special_rotation = cache->special_rotation;
}
static void sculpt_stroke_modifiers_check(bContext *C, SculptSession *ss)
{
- Scene *scene= CTX_data_scene(C);
-
- if(sculpt_modifiers_active(scene, ss->ob)) {
+ if(ss->modifiers_active) {
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = paint_brush(&sd->paint);
@@ -1819,19 +3152,23 @@ typedef struct {
int original;
} SculptRaycastData;
-void sculpt_raycast_cb(PBVHNode *node, void *data_v)
+void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin)
{
- SculptRaycastData *srd = data_v;
- float (*origco)[3]= NULL;
+ if (BLI_pbvh_node_get_tmin(node) < *tmin) {
+ SculptRaycastData *srd = data_v;
+ float (*origco)[3]= NULL;
- if(srd->original && srd->ss->cache) {
- /* intersect with coordinates from before we started stroke */
- SculptUndoNode *unode= sculpt_undo_get_node(srd->ss, node);
- origco= (unode)? unode->co: NULL;
- }
+ if(srd->original && srd->ss->cache) {
+ /* intersect with coordinates from before we started stroke */
+ SculptUndoNode *unode= sculpt_undo_get_node(node);
+ origco= (unode)? unode->co: NULL;
+ }
- srd->hit |= BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco,
- srd->ray_start, srd->ray_normal, &srd->dist);
+ if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, srd->ray_start, srd->ray_normal, &srd->dist)) {
+ srd->hit = 1;
+ *tmin = srd->dist;
+ }
+ }
}
/* Do a raycast in the tree to find the 3d brush location
@@ -1845,10 +3182,12 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou
StrokeCache *cache= ss->cache;
float ray_start[3], ray_end[3], ray_normal[3], dist;
float obimat[4][4];
- float mval[2] = {mouse[0] - vc->ar->winrct.xmin,
- mouse[1] - vc->ar->winrct.ymin};
+ float mval[2];
SculptRaycastData srd;
+ mval[0] = mouse[0] - vc->ar->winrct.xmin;
+ mval[1] = mouse[1] - vc->ar->winrct.ymin;
+
sculpt_stroke_modifiers_check(C, ss);
viewline(vc->ar, vc->v3d, mval, ray_start, ray_end);
@@ -1871,48 +3210,11 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou
copy_v3_v3(out, ray_normal);
mul_v3_fl(out, srd.dist);
- add_v3_v3v3(out, out, ray_start);
+ add_v3_v3(out, ray_start);
return srd.hit;
}
-/* Initialize stroke operator properties */
-static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmEvent *event, SculptSession *ss)
-{
- Object *ob= CTX_data_active_object(C);
- ModifierData *md;
- float scale[3], clip_tolerance[3] = {0,0,0};
- float mouse[2];
- int flag = 0;
-
- /* Set scaling adjustment */
- scale[0] = 1.0f / ob->size[0];
- scale[1] = 1.0f / ob->size[1];
- scale[2] = 1.0f / ob->size[2];
- RNA_float_set_array(op->ptr, "scale", scale);
-
- /* Initialize mirror modifier clipping */
- for(md= ob->modifiers.first; md; md= md->next) {
- if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
- const MirrorModifierData *mmd = (MirrorModifierData*) md;
-
- /* Mark each axis that needs clipping along with its tolerance */
- if(mmd->flag & MOD_MIR_CLIPPING) {
- flag |= CLIP_X << mmd->axis;
- if(mmd->tolerance > clip_tolerance[mmd->axis])
- clip_tolerance[mmd->axis] = mmd->tolerance;
- }
- }
- }
- RNA_int_set(op->ptr, "flag", flag);
- RNA_float_set_array(op->ptr, "clip_tolerance", clip_tolerance);
-
- /* Initial mouse location */
- mouse[0] = event->x;
- mouse[1] = event->y;
- RNA_float_set_array(op->ptr, "initial_mouse", mouse);
-}
-
static int sculpt_brush_stroke_init(bContext *C, ReportList *reports)
{
Scene *scene= CTX_data_scene(C);
@@ -1940,22 +3242,26 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports)
static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
{
- StrokeCache *cache = ss->cache;
Brush *brush = paint_brush(&sd->paint);
- int i;
/* Restore the mesh before continuing with anchored stroke */
- if(brush->flag & BRUSH_ANCHORED) {
+ if((brush->flag & BRUSH_ANCHORED) ||
+ (brush->sculpt_tool == SCULPT_TOOL_GRAB && brush_use_size_pressure(brush)) ||
+ (brush->flag & BRUSH_RESTORE_MESH))
+ {
+ StrokeCache *cache = ss->cache;
+ int i;
+
PBVHNode **nodes;
int n, totnode;
BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
- #pragma omp parallel for private(n) schedule(static)
+ #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
for(n=0; n<totnode; n++) {
SculptUndoNode *unode;
- unode= sculpt_undo_get_node(ss, nodes[n]);
+ unode= sculpt_undo_get_node(nodes[n]);
if(unode) {
PBVHVertexIter vd;
@@ -1963,8 +3269,12 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
copy_v3_v3(vd.co, unode->co[vd.i]);
if(vd.no) VECCOPY(vd.no, unode->no[vd.i])
else normal_short_to_float_v3(vd.fno, unode->no[vd.i]);
+
+ if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
BLI_pbvh_vertex_iter_end;
+
+ BLI_pbvh_node_mark_update(nodes[n]);
}
}
@@ -1981,17 +3291,17 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
static void sculpt_flush_update(bContext *C)
{
- Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
ARegion *ar = CTX_wm_region(C);
MultiresModifierData *mmd = ss->multires;
- int redraw = 0;
if(mmd)
multires_mark_as_modified(ob);
+ if(ob->derivedFinal) /* VBO no longer valid */
+ GPU_drawobject_free(ob->derivedFinal);
- if(sculpt_modifiers_active(scene, ob)) {
+ if(ss->modifiers_active) {
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
ED_region_tag_redraw(ar);
}
@@ -1999,14 +3309,22 @@ static void sculpt_flush_update(bContext *C)
rcti r;
BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL);
- redraw = sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r);
- if(redraw) {
+ if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) {
+ //rcti tmp;
+
r.xmin += ar->winrct.xmin + 1;
r.xmax += ar->winrct.xmin - 1;
r.ymin += ar->winrct.ymin + 1;
r.ymax += ar->winrct.ymin - 1;
-
+
+ //tmp = r;
+
+ //if (!BLI_rcti_is_empty(&ss->previous_r))
+ // BLI_union_rcti(&r, &ss->previous_r);
+
+ //ss->previous_r= tmp;
+
ss->partial_redraw = 1;
ED_region_tag_redraw_partial(ar, &r);
}
@@ -2017,9 +3335,12 @@ static void sculpt_flush_update(bContext *C)
or over the background (0) */
static int over_mesh(bContext *C, struct wmOperator *op, float x, float y)
{
- float mouse[2] = {x, y}, co[3];
-
- return (int)sculpt_stroke_get_location(C, op->customdata, co, mouse);
+ float mouse[2], co[3];
+
+ mouse[0] = x;
+ mouse[1] = y;
+
+ return sculpt_stroke_get_location(C, op->customdata, co, mouse);
}
static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op,
@@ -2033,11 +3354,22 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op,
ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C));
- sculpt_brush_stroke_init_properties(C, op, event, ss);
+ sculpt_update_cache_invariants(C, sd, ss, op, event);
+
+ sculpt_undo_push_begin(sculpt_tool_name(sd));
- sculpt_update_cache_invariants(sd, ss, C, op);
+#ifdef _OPENMP
+ /* If using OpenMP then create a number of threads two times the
+ number of processor cores.
+ Justification: Empirically I've found that two threads per
+ processor gives higher throughput. */
+ if (sd->flags & SCULPT_USE_OPENMP) {
+ int num_procs;
- sculpt_undo_push_begin(ss, sculpt_tool_name(sd));
+ num_procs = omp_get_num_procs();
+ omp_set_num_threads(2*num_procs);
+ }
+#endif
return 1;
}
@@ -2051,7 +3383,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
SculptSession *ss = CTX_data_active_object(C)->sculpt;
sculpt_stroke_modifiers_check(C, ss);
- sculpt_update_cache_variants(sd, ss, stroke, itemptr);
+ sculpt_update_cache_variants(C, sd, ss, stroke, itemptr);
sculpt_restore_mesh(sd, ss);
do_symmetrical_brush_actions(sd, ss);
@@ -2059,26 +3391,46 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
sculpt_flush_update(C);
}
-static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke)
+static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused)
{
Object *ob= CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+
+ (void)unused;
+
+ // reset values used to draw brush after completing the stroke
+ sd->draw_anchored= 0;
+ sd->draw_pressure= 0;
+ sd->special_rotation= 0;
/* Finished */
if(ss->cache) {
sculpt_stroke_modifiers_check(C, ss);
+ /* Alt-Smooth */
+ if (ss->cache->alt_smooth) {
+ Paint *p= &sd->paint;
+ Brush *br= (Brush *)find_id("BR", ss->cache->saved_active_brush_name);
+ if(br) {
+ paint_brush_set(p, br);
+ }
+ }
+
sculpt_cache_free(ss->cache);
ss->cache = NULL;
- sculpt_undo_push_end(ss);
+ sculpt_undo_push_end();
BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL);
- if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob);
+ /* optimization: if there is locked key and active modifiers present in */
+ /* the stack, keyblock is updating at each step. otherwise we could update */
+ /* keyblock only when stroke is finished */
+ if(ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ss);
ss->partial_redraw = 0;
-
+
/* try to avoid calling this, only for e.g. linked duplicates now */
if(((Mesh*)ob->data)->id.us > 1)
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
@@ -2105,6 +3457,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even
/* For tablet rotation */
ignore_background_click = RNA_boolean_get(op->ptr,
"ignore_background_click");
+
if(ignore_background_click && !over_mesh(C, op, event->x, event->y)) {
paint_stroke_free(stroke);
return OPERATOR_PASS_THROUGH;
@@ -2129,7 +3482,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start,
sculpt_stroke_update_step, sculpt_stroke_done);
- sculpt_update_cache_invariants(sd, ss, C, op);
+ sculpt_update_cache_invariants(C, sd, ss, op, NULL);
paint_stroke_exec(C, op);
@@ -2141,7 +3494,12 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
{
- ot->flag |= OPTYPE_REGISTER;
+ static EnumPropertyItem stroke_mode_items[] = {
+ { WM_BRUSHSTROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally" },
+ { WM_BRUSHSTROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke" },
+ { WM_BRUSHSTROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke" },
+ { 0 }
+ };
/* identifiers */
ot->name= "Sculpt Mode";
@@ -2152,36 +3510,32 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
ot->modal= paint_stroke_modal;
ot->exec= sculpt_brush_stroke_exec;
ot->poll= sculpt_poll;
-
+
/* flags (sculpt does own undo? (ton) */
- ot->flag= OPTYPE_REGISTER|OPTYPE_BLOCKING;
+ ot->flag= OPTYPE_BLOCKING;
/* properties */
- RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
-
- /* If the object has a scaling factor, brushes also need to be scaled
- to work as expected. */
- RNA_def_float_vector(ot->srna, "scale", 3, NULL, 0.0f, FLT_MAX, "Scale", "", 0.0f, 1000.0f);
-
- RNA_def_int(ot->srna, "flag", 0, 0, INT_MAX, "flag", "", 0, INT_MAX);
- /* For mirror modifiers */
- RNA_def_float_vector(ot->srna, "clip_tolerance", 3, NULL, 0.0f, FLT_MAX, "clip_tolerance", "", 0.0f, 1000.0f);
+ RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement,
+ "Stroke", "");
- /* The initial 2D location of the mouse */
- RNA_def_float_vector(ot->srna, "initial_mouse", 2, NULL, INT_MIN, INT_MAX, "initial_mouse", "", INT_MIN, INT_MAX);
+ RNA_def_enum(ot->srna, "mode", stroke_mode_items, WM_BRUSHSTROKE_NORMAL,
+ "Sculpt Stroke Mode",
+ "Action taken when a sculpt stroke is made");
RNA_def_boolean(ot->srna, "ignore_background_click", 0,
"Ignore Background Click",
- "Clicks on the background don't start the stroke");
+ "Clicks on the background do not start the stroke");
}
/**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/
-static int sculpt_set_persistent_base(bContext *C, wmOperator *op)
+static int sculpt_set_persistent_base(bContext *C, wmOperator *unused)
{
SculptSession *ss = CTX_data_active_object(C)->sculpt;
+ (void)unused;
+
if(ss) {
if(ss->layer_co)
MEM_freeN(ss->layer_co);
@@ -2201,7 +3555,7 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot)
ot->exec= sculpt_set_persistent_base;
ot->poll= sculpt_mode_poll;
- ot->flag= OPTYPE_REGISTER;
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
/**** Toggle operator for turning sculpt mode on or off ****/
@@ -2209,25 +3563,31 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot)
static void sculpt_init_session(Scene *scene, Object *ob)
{
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
-
- sculpt_update_mesh_elements(scene, ob, 0);
+ ob->sculpt->ob = ob;
- if(ob->sculpt->refkb)
- sculpt_key_to_mesh(ob->sculpt->refkb, ob);
+ sculpt_update_mesh_elements(scene, ob, 0);
}
-static int sculpt_toggle_mode(bContext *C, wmOperator *op)
+static int sculpt_toggle_mode(bContext *C, wmOperator *unused)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
- MultiresModifierData *mmd = sculpt_multires_active(ob);
+ MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
+ int flush_recalc= 0;
+
+ (void)unused;
+
+ /* multires in sculpt mode could have different from object mode subdivision level */
+ flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl;
+ /* if object has got active modifiers, it's dm could be different in sculpt mode */
+ //flush_recalc |= sculpt_modifiers_active(scene, ob);
if(ob->mode & OB_MODE_SCULPT) {
- if(sculpt_multires_active(ob))
+ if(mmd)
multires_force_update(ob);
- if(mmd && mmd->sculptlvl != mmd->lvl)
+ if(flush_recalc)
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
/* Leave sculptmode */
@@ -2239,13 +3599,17 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op)
/* Enter sculptmode */
ob->mode |= OB_MODE_SCULPT;
- if(mmd && mmd->sculptlvl != mmd->lvl)
+ if(flush_recalc)
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
/* Create persistent sculpt mode data */
- if(!ts->sculpt)
+ if(!ts->sculpt) {
ts->sculpt = MEM_callocN(sizeof(Sculpt), "sculpt mode data");
+ /* Turn on X plane mirror symmetry by default */
+ ts->sculpt->flags |= SCULPT_SYMM_X;
+ }
+
/* Create sculpt mode session data */
if(ob->sculpt)
free_sculptsession(ob);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index d3553c008b2..254876d9b68 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -32,6 +32,9 @@
#include "DNA_listBase.h"
#include "DNA_vec_types.h"
+#include "DNA_key_types.h"
+
+#include "BLI_pbvh.h"
struct bContext;
struct Brush;
@@ -49,7 +52,7 @@ void sculptmode_draw_mesh(int);
void sculpt_paint_brush(char clear);
void sculpt_stroke_draw(struct SculptStroke *);
void sculpt_radialcontrol_start(int mode);
-struct MultiresModifierData *sculpt_multires_active(struct Object *ob);
+struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob);
struct Brush *sculptmode_brush(void);
//void do_symmetrical_brush_actions(struct Sculpt *sd, struct wmOperator *wm, struct BrushAction *a, short *, short *);
@@ -58,8 +61,6 @@ void sculpt(Sculpt *sd);
int sculpt_poll(struct bContext *C);
void sculpt_update_mesh_elements(struct Scene *scene, struct Object *ob, int need_fmap);
-void sculpt_key_to_mesh(struct KeyBlock *kb, struct Object *ob);
-void sculpt_mesh_to_key(struct Object *ob, struct KeyBlock *kb);
/* Stroke */
struct SculptStroke *sculpt_stroke_new(const int max);
@@ -67,8 +68,49 @@ 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]);
/* Partial Mesh Visibility */
void sculptmode_pmv(int mode);
+/* Undo */
+
+typedef struct SculptUndoNode {
+ struct SculptUndoNode *next, *prev;
+
+ char idname[MAX_ID_NAME]; /* name instead of pointer*/
+ void *node; /* only during push, not valid afterwards! */
+
+ float (*co)[3];
+ short (*no)[3];
+ int totvert;
+
+ /* non-multires */
+ int maxvert; /* to verify if totvert it still the same */
+ int *index; /* to restore into right location */
+
+ /* multires */
+ int maxgrid; /* same for grid */
+ int gridsize; /* same for grid */
+ int totgrid; /* to restore into right location */
+ int *grids; /* to restore into right location */
+
+ /* layer brush */
+ float *layer_disp;
+
+ /* shape keys */
+ char *shapeName[32]; /* keep size in sync with keyblock dna */
+} SculptUndoNode;
+
+SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node);
+SculptUndoNode *sculpt_undo_get_node(PBVHNode *node);
+void sculpt_undo_push_begin(char *name);
+void sculpt_undo_push_end(void);
+
+struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob);
+int sculpt_modifiers_active(Scene *scene, Object *ob);
+void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]);
+
+void brush_jitter_pos(struct Brush *brush, float *pos, float *jitterpos);
+
#endif
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
new file mode 100644
index 00000000000..e92740678fd
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -0,0 +1,302 @@
+/*
+ * $Id$
+ *
+ * ***** 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) 2006 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ * Implements the Sculpt Mode tools
+ *
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_ghash.h"
+#include "BLI_threads.h"
+
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_mesh_types.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_paint.h"
+#include "BKE_key.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+/************************** Undo *************************/
+
+static void update_cb(PBVHNode *node, void *unused)
+{
+ (void)unused;
+ BLI_pbvh_node_mark_update(node);
+}
+
+static void sculpt_undo_restore(bContext *C, ListBase *lb)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
+ SculptSession *ss = ob->sculpt;
+ SculptUndoNode *unode;
+ MVert *mvert;
+ MultiresModifierData *mmd;
+ int *index;
+ int i, j, update= 0;
+
+ sculpt_update_mesh_elements(scene, ob, 0);
+
+ for(unode=lb->first; unode; unode=unode->next) {
+ if(!(strcmp(unode->idname, ob->id.name)==0))
+ continue;
+
+ if(unode->maxvert) {
+ char *shapeName= (char*)unode->shapeName;
+
+ /* regular mesh restore */
+ if(ss->totvert != unode->maxvert)
+ continue;
+
+ if (ss->kb && strcmp(ss->kb->name, shapeName)) {
+ /* shape key has been changed before calling undo operator */
+
+ Key *key= ob_get_key(ob);
+ KeyBlock *kb= key_get_named_keyblock(key, shapeName);
+
+ if (kb) {
+ ob->shapenr= BLI_findindex(&key->block, kb) + 1;
+ ob->shapeflag|= OB_SHAPE_LOCK;
+
+ sculpt_update_mesh_elements(scene, ob, 0);
+ WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob);
+ } else {
+ /* key has been removed -- skip this undo node */
+ continue;
+ }
+ }
+
+ index= unode->index;
+ mvert= ss->mvert;
+
+ if (ss->kb) {
+ float (*vertCos)[3];
+ vertCos= key_to_vertcos(ob, ss->kb);
+
+ for(i=0; i<unode->totvert; i++)
+ swap_v3_v3(vertCos[index[i]], unode->co[i]);
+
+ /* propagate new coords to keyblock */
+ sculpt_vertcos_to_key(ob, ss->kb, vertCos);
+
+ /* pbvh uses it's own mvert array, so coords should be */
+ /* propagated to pbvh here */
+ BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
+
+ MEM_freeN(vertCos);
+ } else {
+ for(i=0; i<unode->totvert; i++) {
+ swap_v3_v3(mvert[index[i]].co, unode->co[i]);
+ mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ else if(unode->maxgrid && dm->getGridData) {
+ /* multires restore */
+ DMGridData **grids, *grid;
+ float (*co)[3];
+ int gridsize;
+
+ if(dm->getNumGrids(dm) != unode->maxgrid)
+ continue;
+ if(dm->getGridSize(dm) != unode->gridsize)
+ continue;
+
+ grids= dm->getGridData(dm);
+ gridsize= dm->getGridSize(dm);
+
+ co = unode->co;
+ for(j=0; j<unode->totgrid; j++) {
+ grid= grids[unode->grids[j]];
+
+ for(i=0; i<gridsize*gridsize; i++, co++)
+ swap_v3_v3(grid[i].co, co[0]);
+ }
+ }
+
+ update= 1;
+ }
+
+ if(update) {
+ /* we update all nodes still, should be more clever, but also
+ needs to work correct when exiting/entering sculpt mode and
+ the nodes get recreated, though in that case it could do all */
+ BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, NULL);
+ BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL);
+
+ if((mmd=sculpt_multires_active(scene, ob)))
+ multires_mark_as_modified(ob);
+
+ if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1)
+ DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+ }
+}
+
+static void sculpt_undo_free(ListBase *lb)
+{
+ SculptUndoNode *unode;
+
+ for(unode=lb->first; unode; unode=unode->next) {
+ if(unode->co)
+ MEM_freeN(unode->co);
+ if(unode->no)
+ MEM_freeN(unode->no);
+ if(unode->index)
+ MEM_freeN(unode->index);
+ if(unode->grids)
+ MEM_freeN(unode->grids);
+ if(unode->layer_disp)
+ MEM_freeN(unode->layer_disp);
+ }
+}
+
+SculptUndoNode *sculpt_undo_get_node(PBVHNode *node)
+{
+ ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
+ SculptUndoNode *unode;
+
+ if(!lb)
+ return NULL;
+
+ for(unode=lb->first; unode; unode=unode->next)
+ if(unode->node == node)
+ return unode;
+
+ return NULL;
+}
+
+SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node)
+{
+ ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
+ Object *ob= ss->ob;
+ SculptUndoNode *unode;
+ int totvert, allvert, totgrid, maxgrid, gridsize, *grids;
+
+ /* list is manipulated by multiple threads, so we lock */
+ BLI_lock_thread(LOCK_CUSTOM1);
+
+ if((unode= sculpt_undo_get_node(node))) {
+ BLI_unlock_thread(LOCK_CUSTOM1);
+ return unode;
+ }
+
+ unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode");
+ strcpy(unode->idname, ob->id.name);
+ unode->node= node;
+
+ BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert);
+ BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid,
+ &maxgrid, &gridsize, NULL, NULL);
+
+ unode->totvert= totvert;
+ /* we will use this while sculpting, is mapalloc slow to access then? */
+ unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co");
+ unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no");
+ undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert);
+ BLI_addtail(lb, unode);
+
+ if(maxgrid) {
+ /* multires */
+ unode->maxgrid= maxgrid;
+ unode->totgrid= totgrid;
+ unode->gridsize= gridsize;
+ unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids");
+ }
+ else {
+ /* regular mesh */
+ unode->maxvert= ss->totvert;
+ unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index");
+ }
+
+ BLI_unlock_thread(LOCK_CUSTOM1);
+
+ /* copy threaded, hopefully this is the performance critical part */
+ {
+ PBVHVertexIter vd;
+
+ BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) {
+ copy_v3_v3(unode->co[vd.i], vd.co);
+ if(vd.no) VECCOPY(unode->no[vd.i], vd.no)
+ else normal_float_to_short_v3(unode->no[vd.i], vd.fno);
+ if(vd.vert_indices) unode->index[vd.i]= vd.vert_indices[vd.i];
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+
+ if(unode->grids)
+ memcpy(unode->grids, grids, sizeof(int)*totgrid);
+
+ /* store active shape key */
+ if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name));
+ else unode->shapeName[0]= '\0';
+
+ return unode;
+}
+
+void sculpt_undo_push_begin(char *name)
+{
+ undo_paint_push_begin(UNDO_PAINT_MESH, name,
+ sculpt_undo_restore, sculpt_undo_free);
+}
+
+void sculpt_undo_push_end(void)
+{
+ ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
+ SculptUndoNode *unode;
+
+ /* we don't need normals in the undo stack */
+ for(unode=lb->first; unode; unode=unode->next) {
+ if(unode->no) {
+ MEM_freeN(unode->no);
+ unode->no= NULL;
+ }
+
+ if(unode->layer_disp) {
+ MEM_freeN(unode->layer_disp);
+ unode->layer_disp= NULL;
+ }
+ }
+
+ undo_paint_push_end(UNDO_PAINT_MESH);
+}