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:
-rw-r--r--build_files/buildbot/master.cfg5
-rw-r--r--build_files/buildbot/slave_compile.py6
-rw-r--r--build_files/buildbot/slave_pack.py8
-rw-r--r--intern/cycles/kernel/CMakeLists.txt20
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py29
-rw-r--r--source/blender/blenkernel/BKE_brush.h2
-rw-r--r--source/blender/blenkernel/BKE_paint.h5
-rw-r--r--source/blender/blenkernel/intern/brush.c2
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c10
-rw-r--r--source/blender/blenkernel/intern/paint.c7
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c34
-rw-r--r--source/blender/blenlib/BLI_math_geom.h20
-rw-r--r--source/blender/blenlib/intern/math_geom.c146
-rw-r--r--source/blender/blenloader/intern/versioning_270.c31
-rw-r--r--source/blender/compositor/intern/COM_Debug.cpp4
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.cpp19
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cpp74
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h2
-rw-r--r--source/blender/editors/mesh/meshtools.c9
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c661
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_utils.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c12
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c117
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c140
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h21
-rw-r--r--source/blender/imbuf/intern/tiff.c43
-rw-r--r--source/blender/makesdna/DNA_scene_types.h22
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c33
-rw-r--r--source/blender/windowmanager/WM_types.h1
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c3
30 files changed, 1037 insertions, 451 deletions
diff --git a/build_files/buildbot/master.cfg b/build_files/buildbot/master.cfg
index c721d5a4ba9..e3101430e50 100644
--- a/build_files/buildbot/master.cfg
+++ b/build_files/buildbot/master.cfg
@@ -245,8 +245,7 @@ def git_submodules_update():
def lib_svn_step(dir):
- name = "lib svn"
- return SVN(name=name,
+ return SVN(name='lib svn',
baseURL='https://svn.blender.org/svnroot/bf-blender/%%BRANCH%%/lib/' + dir,
codebase='lib svn',
mode='update',
@@ -307,7 +306,7 @@ def generic_builder(id, libdir='', branch='', rsync=False):
# Builders
-add_builder(c, 'mac_x86_64_10_6_cmake', 'darwin', generic_builder, hour=1)
+add_builder(c, 'mac_x86_64_10_9_cmake', 'darwin', generic_builder, hour=1)
add_builder(c, 'linux_glibc219_i686_cmake', '', generic_builder, hour=2)
add_builder(c, 'linux_glibc219_x86_64_cmake', '', generic_builder, hour=1)
add_builder(c, 'win32_cmake_vc2013', 'windows_vc12', generic_builder, hour=1)
diff --git a/build_files/buildbot/slave_compile.py b/build_files/buildbot/slave_compile.py
index 84f924b2dd9..1318cd26294 100644
--- a/build_files/buildbot/slave_compile.py
+++ b/build_files/buildbot/slave_compile.py
@@ -70,14 +70,12 @@ if 'cmake' in builder:
if builder.startswith('mac'):
# Set up OSX architecture
- if builder.endswith('x86_64_10_6_cmake'):
+ if builder.endswith('x86_64_10_9_cmake'):
cmake_extra_options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64')
- cmake_extra_options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6')
+ cmake_extra_options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9')
cmake_extra_options.append('-DCUDA_HOST_COMPILER=/usr/local/cuda-hack/clang')
cmake_extra_options.append('-DCUDA_NVCC_EXECUTABLE=/usr/local/cuda-hack/nvcc')
-
-
elif builder.startswith('win'):
if builder.endswith('_vc2015'):
if builder.startswith('win64'):
diff --git a/build_files/buildbot/slave_pack.py b/build_files/buildbot/slave_pack.py
index 6929be85003..41841d809eb 100644
--- a/build_files/buildbot/slave_pack.py
+++ b/build_files/buildbot/slave_pack.py
@@ -101,13 +101,9 @@ if builder.find('cmake') != -1:
platform = builder.split('_')[0]
if platform == 'mac':
# Special exception for OSX
- platform = 'OSX-10.6-'
- if builder.endswith('x86_64_10_6_cmake'):
+ platform = 'OSX-10.9-'
+ if builder.endswith('x86_64_10_9_cmake'):
platform += 'x86_64'
- elif builder.endswith('i386_10_6_cmake'):
- platform += 'i386'
- elif builder.endswith('ppc_10_6_cmake'):
- platform += 'ppc'
if builder.endswith('vc2015'):
platform += "-vc14"
builderified_name = 'blender-{}-{}-{}'.format(blender_full_version, git_hash, platform)
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index b4ca16bdb48..b10dd05cb9b 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -321,7 +321,7 @@ if(WITH_CYCLES_CUDA_BINARIES)
set(CUDA_VERSION "${CUDA_VERSION_MAJOR}${CUDA_VERSION_MINOR}")
# warn for other versions
- if(CUDA_VERSION MATCHES "80")
+ if(CUDA_VERSION MATCHES "80" OR CUDA_VERSION MATCHES "90")
else()
message(WARNING
"CUDA version ${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR} detected, "
@@ -399,13 +399,17 @@ if(WITH_CYCLES_CUDA_BINARIES)
endmacro()
foreach(arch ${CYCLES_CUDA_BINARIES_ARCH})
- # Compile regular kernel
- CYCLES_CUDA_KERNEL_ADD(${arch} kernel "" "${cuda_sources}" FALSE)
- CYCLES_CUDA_KERNEL_ADD(${arch} filter "" "${cuda_filter_sources}" FALSE)
-
- if(WITH_CYCLES_CUDA_SPLIT_KERNEL_BINARIES)
- # Compile split kernel
- CYCLES_CUDA_KERNEL_ADD(${arch} kernel_split "-D__SPLIT__" ${cuda_sources} FALSE)
+ if(CUDA_VERSION MATCHES "90" AND ${arch} MATCHES "sm_2.")
+ message(STATUS "CUDA binaries for ${arch} disabled, not supported by CUDA 9.")
+ else()
+ # Compile regular kernel
+ CYCLES_CUDA_KERNEL_ADD(${arch} kernel "" "${cuda_sources}" FALSE)
+ CYCLES_CUDA_KERNEL_ADD(${arch} filter "" "${cuda_filter_sources}" FALSE)
+
+ if(WITH_CYCLES_CUDA_SPLIT_KERNEL_BINARIES)
+ # Compile split kernel
+ CYCLES_CUDA_KERNEL_ADD(${arch} kernel_split "-D__SPLIT__" ${cuda_sources} FALSE)
+ endif()
endif()
endforeach()
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 0114d96af48..b7e2b5b6051 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -1761,9 +1761,16 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel):
wpaint = tool_settings.weight_paint
col = layout.column()
+ col.label("Falloff:")
row = col.row()
-
- row.prop(wpaint, "use_normal")
+ row.prop(wpaint, "falloff_shape", expand=True)
+ row = col.row()
+ row.prop(wpaint, "use_backface_culling")
+ row = col.row()
+ row.prop(wpaint, "use_normal_falloff")
+ sub = row.row()
+ sub.active = (wpaint.use_normal_falloff)
+ sub.prop(wpaint, "normal_angle", text="")
col = layout.column()
row = col.row()
row.prop(wpaint, "use_spray")
@@ -1798,19 +1805,21 @@ class VIEW3D_PT_tools_vertexpaint(Panel, View3DPaintPanel):
vpaint = toolsettings.vertex_paint
col = layout.column()
+ col.label("Falloff:")
+ row = col.row()
+ row.prop(vpaint, "falloff_shape", expand=True)
row = col.row()
- # col.prop(vpaint, "mode", text="")
- row.prop(vpaint, "use_normal")
+ row.prop(vpaint, "use_backface_culling")
+ row = col.row()
+ row.prop(vpaint, "use_normal_falloff")
+ sub = row.row()
+ sub.active = (vpaint.use_normal_falloff)
+ sub.prop(vpaint, "normal_angle", text="")
+
col.prop(vpaint, "use_spray")
self.unified_paint_settings(col, context)
-# Commented out because the Apply button isn't an operator yet, making these settings useless
-#~ col.label(text="Gamma:")
-#~ col.prop(vpaint, "gamma", text="")
-#~ col.label(text="Multiply:")
-#~ col.prop(vpaint, "mul", text="")
-
class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel):
bl_category = "Tools"
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h
index dedb75a080a..c1e107e101a 100644
--- a/source/blender/blenkernel/BKE_brush.h
+++ b/source/blender/blenkernel/BKE_brush.h
@@ -70,7 +70,7 @@ void BKE_brush_randomize_texture_coords(struct UnifiedPaintSettings *ups, bool m
/* brush curve */
void BKE_brush_curve_preset(struct Brush *b, int preset);
float BKE_brush_curve_strength_clamped(struct Brush *br, float p, const float len);
-float BKE_brush_curve_strength(struct Brush *br, float p, const float len);
+float BKE_brush_curve_strength(const struct Brush *br, float p, const float len);
/* sampling */
float BKE_brush_sample_tex_3D(
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 5c7684f9fe1..527c0aeb609 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -230,9 +230,12 @@ typedef struct SculptSession {
/* Vertex aligned arrays of weights. */
float *previous_accum;
- float *previous_weight;
/* Keep track of how much each vertex has been painted (non-airbrush only). */
float *alpha_weight;
+
+ /* Needed to continuously re-apply over the same weights (VP_FLAG_SPRAY disabled).
+ * Lazy initialize as needed (flag is set to 1 to tag it as uninitialized). */
+ struct MDeformVert *dvert_prev;
} wpaint;
//struct {
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 03b0710c8fc..0300dfe5891 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -966,7 +966,7 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
}
/* Uses the brush curve control to find a strength value */
-float BKE_brush_curve_strength(Brush *br, float p, const float len)
+float BKE_brush_curve_strength(const Brush *br, float p, const float len)
{
float strength;
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index dd43bf8b9d8..d1282c1a0fe 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -641,18 +641,8 @@ static void cdDM_drawMappedFaces(
/* avoid buffer problems in following code */
}
else if (setDrawOptions == NULL) {
- const bool show_alpha = true;
- if (show_alpha) {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
/* just draw the entire face array */
GPU_buffer_draw_elements(dm->drawObject->triangles, GL_TRIANGLES, 0, tot_tri_elem);
-
- if (show_alpha) {
- glDisable(GL_BLEND);
- }
}
else {
for (mat_index = 0; mat_index < dm->drawObject->totmaterial; mat_index++) {
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 8c336002ba3..40d02acab1c 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -49,6 +49,7 @@
#include "BKE_brush.h"
#include "BKE_colortools.h"
+#include "BKE_deform.h"
#include "BKE_main.h"
#include "BKE_context.h"
#include "BKE_crazyspace.h"
@@ -689,8 +690,12 @@ void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss)
gmap = &ss->mode.wpaint.gmap;
MEM_SAFE_FREE(ss->mode.wpaint.alpha_weight);
- MEM_SAFE_FREE(ss->mode.wpaint.previous_weight);
MEM_SAFE_FREE(ss->mode.wpaint.previous_accum);
+ if (ss->mode.wpaint.dvert_prev) {
+ BKE_defvert_array_free_elems(ss->mode.wpaint.dvert_prev, ss->totvert);
+ MEM_freeN(ss->mode.wpaint.dvert_prev);
+ ss->mode.wpaint.dvert_prev = NULL;
+ }
}
else {
return;
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 764df797ef6..d461e03fcda 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -2199,12 +2199,12 @@ static void ccgDM_buffer_copy_color(
for (S = 0; S < numVerts; S++) {
for (y = 0; y < gridFaces; y++) {
for (x = 0; x < gridFaces; x++) {
- copy_v3_v3_uchar(&varray[start + 0], &mloopcol[iface * 16 + 0]);
- copy_v3_v3_uchar(&varray[start + 3], &mloopcol[iface * 16 + 12]);
- copy_v3_v3_uchar(&varray[start + 6], &mloopcol[iface * 16 + 8]);
- copy_v3_v3_uchar(&varray[start + 9], &mloopcol[iface * 16 + 4]);
+ copy_v4_v4_uchar(&varray[start + 0], &mloopcol[iface * 16 + 0]);
+ copy_v4_v4_uchar(&varray[start + 4], &mloopcol[iface * 16 + 12]);
+ copy_v4_v4_uchar(&varray[start + 8], &mloopcol[iface * 16 + 8]);
+ copy_v4_v4_uchar(&varray[start + 12], &mloopcol[iface * 16 + 4]);
- start += 12;
+ start += 16;
iface++;
}
}
@@ -3544,16 +3544,16 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm,
float *c = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1);
float *d = CCG_grid_elem_co(&key, faceGridData, x, y + 1);
- if (cp) glColor3ubv(&cp[4]);
+ if (cp) glColor4ubv(&cp[4]);
glNormal3fv(ln[1]);
glVertex3fv(d);
- if (cp) glColor3ubv(&cp[8]);
+ if (cp) glColor4ubv(&cp[8]);
glNormal3fv(ln[2]);
glVertex3fv(c);
- if (cp) glColor3ubv(&cp[12]);
+ if (cp) glColor4ubv(&cp[12]);
glNormal3fv(ln[3]);
glVertex3fv(b);
- if (cp) glColor3ubv(&cp[0]);
+ if (cp) glColor4ubv(&cp[0]);
glNormal3fv(ln[0]);
glVertex3fv(a);
@@ -3571,10 +3571,10 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm,
a = CCG_grid_elem(&key, faceGridData, x, y + 0);
b = CCG_grid_elem(&key, faceGridData, x, y + 1);
- if (cp) glColor3ubv(&cp[0]);
+ if (cp) glColor4ubv(&cp[0]);
glNormal3fv(CCG_elem_no(&key, a));
glVertex3fv(CCG_elem_co(&key, a));
- if (cp) glColor3ubv(&cp[4]);
+ if (cp) glColor4ubv(&cp[4]);
glNormal3fv(CCG_elem_no(&key, b));
glVertex3fv(CCG_elem_co(&key, b));
@@ -3586,10 +3586,10 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm,
a = CCG_grid_elem(&key, faceGridData, x, y + 0);
b = CCG_grid_elem(&key, faceGridData, x, y + 1);
- if (cp) glColor3ubv(&cp[12]);
+ if (cp) glColor4ubv(&cp[12]);
glNormal3fv(CCG_elem_no(&key, a));
glVertex3fv(CCG_elem_co(&key, a));
- if (cp) glColor3ubv(&cp[8]);
+ if (cp) glColor4ubv(&cp[8]);
glNormal3fv(CCG_elem_no(&key, b));
glVertex3fv(CCG_elem_co(&key, b));
@@ -3609,13 +3609,13 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm,
ccgDM_glNormalFast(a, b, c, d);
- if (cp) glColor3ubv(&cp[4]);
+ if (cp) glColor4ubv(&cp[4]);
glVertex3fv(d);
- if (cp) glColor3ubv(&cp[8]);
+ if (cp) glColor4ubv(&cp[8]);
glVertex3fv(c);
- if (cp) glColor3ubv(&cp[12]);
+ if (cp) glColor4ubv(&cp[12]);
glVertex3fv(b);
- if (cp) glColor3ubv(&cp[0]);
+ if (cp) glColor4ubv(&cp[0]);
glVertex3fv(a);
if (cp) cp += 16;
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 0fef849c8fa..d0b59244384 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -119,6 +119,26 @@ float dist_squared_ray_to_seg_v3(
const float ray_origin[3], const float ray_direction[3],
const float v0[3], const float v1[3],
float r_point[3], float *r_depth);
+
+struct DistRayAABB_Precalc {
+ float ray_origin[3];
+ float ray_direction[3];
+ float ray_inv_dir[3];
+ bool sign[3];
+};
+void dist_squared_ray_to_aabb_precalc(
+ struct DistRayAABB_Precalc *neasrest_precalc,
+ const float ray_origin[3], const float ray_direction[3]);
+float dist_squared_ray_to_aabb(
+ const struct DistRayAABB_Precalc *data,
+ const float bb_min[3], const float bb_max[3],
+ float r_point[3], float *r_depth);
+/* when there is no advantage to precalc. */
+float dist_squared_to_ray_to_aabb_simple(
+ const float ray_origin[3], const float ray_direction[3],
+ const float bb_min[3], const float bb_max[3],
+ float r_point[3], float *r_depth);
+
float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2]);
float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3]);
void closest_to_line_segment_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2]);
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 53fcf9c745c..dbbc1adb534 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -619,6 +619,152 @@ float dist_squared_ray_to_seg_v3(
return len_squared_v3(t) - SQUARE(*r_depth);
}
+/* -------------------------------------------------------------------- */
+/** \name dist_squared_to_ray_to_aabb and helpers
+ * \{ */
+
+void dist_squared_ray_to_aabb_precalc(
+ struct DistRayAABB_Precalc *neasrest_precalc,
+ const float ray_origin[3], const float ray_direction[3])
+{
+ copy_v3_v3(neasrest_precalc->ray_origin, ray_origin);
+ copy_v3_v3(neasrest_precalc->ray_direction, ray_direction);
+
+ for (int i = 0; i < 3; i++) {
+ neasrest_precalc->ray_inv_dir[i] =
+ (neasrest_precalc->ray_direction[i] != 0.0f) ?
+ (1.0f / neasrest_precalc->ray_direction[i]) : FLT_MAX;
+ neasrest_precalc->sign[i] = (neasrest_precalc->ray_inv_dir[i] < 0.0f);
+ }
+}
+
+/**
+ * Returns the distance from a ray to a bound-box (projected on ray)
+ */
+float dist_squared_ray_to_aabb(
+ const struct DistRayAABB_Precalc *data,
+ const float bb_min[3], const float bb_max[3],
+ float r_point[3], float *r_depth)
+{
+ // bool r_axis_closest[3];
+ float local_bvmin[3], local_bvmax[3];
+ if (data->sign[0]) {
+ local_bvmin[0] = bb_max[0];
+ local_bvmax[0] = bb_min[0];
+ }
+ else {
+ local_bvmin[0] = bb_min[0];
+ local_bvmax[0] = bb_max[0];
+ }
+ if (data->sign[1]) {
+ local_bvmin[1] = bb_max[1];
+ local_bvmax[1] = bb_min[1];
+ }
+ else {
+ local_bvmin[1] = bb_min[1];
+ local_bvmax[1] = bb_max[1];
+ }
+ if (data->sign[2]) {
+ local_bvmin[2] = bb_max[2];
+ local_bvmax[2] = bb_min[2];
+ }
+ else {
+ local_bvmin[2] = bb_min[2];
+ local_bvmax[2] = bb_max[2];
+ }
+
+ const float tmin[3] = {
+ (local_bvmin[0] - data->ray_origin[0]) * data->ray_inv_dir[0],
+ (local_bvmin[1] - data->ray_origin[1]) * data->ray_inv_dir[1],
+ (local_bvmin[2] - data->ray_origin[2]) * data->ray_inv_dir[2],
+ };
+ const float tmax[3] = {
+ (local_bvmax[0] - data->ray_origin[0]) * data->ray_inv_dir[0],
+ (local_bvmax[1] - data->ray_origin[1]) * data->ray_inv_dir[1],
+ (local_bvmax[2] - data->ray_origin[2]) * data->ray_inv_dir[2],
+ };
+ /* `va` and `vb` are the coordinates of the AABB edge closest to the ray */
+ float va[3], vb[3];
+ /* `rtmin` and `rtmax` are the minimum and maximum distances of the ray hits on the AABB */
+ float rtmin, rtmax;
+ int main_axis;
+
+ if ((tmax[0] <= tmax[1]) && (tmax[0] <= tmax[2])) {
+ rtmax = tmax[0];
+ va[0] = vb[0] = local_bvmax[0];
+ main_axis = 3;
+ // r_axis_closest[0] = data->sign[0];
+ }
+ else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) {
+ rtmax = tmax[1];
+ va[1] = vb[1] = local_bvmax[1];
+ main_axis = 2;
+ // r_axis_closest[1] = data->sign[1];
+ }
+ else {
+ rtmax = tmax[2];
+ va[2] = vb[2] = local_bvmax[2];
+ main_axis = 1;
+ // r_axis_closest[2] = data->sign[2];
+ }
+
+ if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) {
+ rtmin = tmin[0];
+ va[0] = vb[0] = local_bvmin[0];
+ main_axis -= 3;
+ // r_axis_closest[0] = !data->sign[0];
+ }
+ else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) {
+ rtmin = tmin[1];
+ va[1] = vb[1] = local_bvmin[1];
+ main_axis -= 1;
+ // r_axis_closest[1] = !data->sign[1];
+ }
+ else {
+ rtmin = tmin[2];
+ va[2] = vb[2] = local_bvmin[2];
+ main_axis -= 2;
+ // r_axis_closest[2] = !data->sign[2];
+ }
+ if (main_axis < 0) {
+ main_axis += 3;
+ }
+
+ /* if rtmin <= rtmax, ray intersect `AABB` */
+ if (rtmin <= rtmax) {
+ float dvec[3];
+ copy_v3_v3(r_point, local_bvmax);
+ sub_v3_v3v3(dvec, local_bvmax, data->ray_origin);
+ *r_depth = dot_v3v3(dvec, data->ray_direction);
+ return 0.0f;
+ }
+
+ if (data->sign[main_axis]) {
+ va[main_axis] = local_bvmax[main_axis];
+ vb[main_axis] = local_bvmin[main_axis];
+ }
+ else {
+ va[main_axis] = local_bvmin[main_axis];
+ vb[main_axis] = local_bvmax[main_axis];
+ }
+
+ return dist_squared_ray_to_seg_v3(
+ data->ray_origin, data->ray_direction, va, vb,
+ r_point, r_depth);
+}
+
+float dist_squared_to_ray_to_aabb_simple(
+ const float ray_origin[3], const float ray_direction[3],
+ const float bbmin[3], const float bbmax[3],
+ float r_point[3], float *r_depth)
+{
+ struct DistRayAABB_Precalc data;
+ dist_squared_ray_to_aabb_precalc(&data, ray_origin, ray_direction);
+ return dist_squared_ray_to_aabb(&data, bbmin, bbmax, r_point, r_depth);
+}
+/** \} */
+
+
/* Adapted from "Real-Time Collision Detection" by Christer Ericson,
* published by Morgan Kaufmann Publishers, copyright 2005 Elsevier Inc.
*
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index afbd84bdb91..bab1a45796d 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -1653,23 +1653,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
- {
- Brush *br;
- br = (Brush *)BKE_libblock_find_name_ex(main, ID_BR, "Average");
- if (!br) {
- br = BKE_brush_add(main, "Average", OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT);
- br->vertexpaint_tool = PAINT_BLEND_AVERAGE;
- br->ob_mode = OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT;
- }
-
- br = (Brush *)BKE_libblock_find_name_ex(main, ID_BR, "Smear");
- if (!br) {
- br = BKE_brush_add(main, "Smear", OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT);
- br->vertexpaint_tool = PAINT_BLEND_SMEAR;
- br->ob_mode = OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT;
- }
- }
-
FOREACH_NODETREE(main, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
do_versions_compositor_render_passes(ntree);
@@ -1720,6 +1703,20 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
+
+ if (!DNA_struct_elem_find(fd->filesdna, "VPaint", "char", "falloff_shape")) {
+ for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+ ToolSettings *ts = scene->toolsettings;
+ for (int i = 0; i < 2; i++) {
+ VPaint *vp = i ? ts->vpaint : ts->wpaint;
+ if (vp != NULL) {
+ /* remove all other flags */
+ vp->flag &= (VP_FLAG_SPRAY | VP_FLAG_VGROUP_RESTRICT);
+ vp->normal_angle = 80;
+ }
+ }
+ }
+ }
}
}
diff --git a/source/blender/compositor/intern/COM_Debug.cpp b/source/blender/compositor/intern/COM_Debug.cpp
index a7b464cde29..68439ff8469 100644
--- a/source/blender/compositor/intern/COM_Debug.cpp
+++ b/source/blender/compositor/intern/COM_Debug.cpp
@@ -28,9 +28,11 @@
#include <vector>
extern "C" {
+#include "BLI_sys_types.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
+
#include "DNA_node_types.h"
#include "BKE_appdir.h"
#include "BKE_node.h"
@@ -174,7 +176,7 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system, const NodeOpera
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "%s\\n(%s)", m_op_names[operation].c_str(), typeid(*operation).name());
- len += snprintf(str + len, maxlen > len ? maxlen - len : 0, " (%d,%d)", operation->getWidth(), operation->getHeight());
+ len += snprintf(str + len, maxlen > len ? maxlen - len : 0, " (%u,%u)", operation->getWidth(), operation->getHeight());
int totoutputs = operation->getNumberOfOutputSockets();
if (totoutputs != 0) {
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cpp b/source/blender/compositor/nodes/COM_ScaleNode.cpp
index 61eea9227dc..ef4128a78b4 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.cpp
+++ b/source/blender/compositor/nodes/COM_ScaleNode.cpp
@@ -52,6 +52,9 @@ void ScaleNode::convertToOperations(NodeConverter &converter, const CompositorCo
converter.mapInputSocket(inputXSocket, operation->getInputSocket(1));
converter.mapInputSocket(inputYSocket, operation->getInputSocket(2));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
+
+ operation->setVariableSize(inputXSocket->isLinked() ||
+ inputYSocket->isLinked());
break;
}
case CMP_SCALE_SCENEPERCENT:
@@ -67,6 +70,10 @@ void ScaleNode::convertToOperations(NodeConverter &converter, const CompositorCo
converter.addLink(scaleFactorOperation->getOutputSocket(), operation->getInputSocket(1));
converter.addLink(scaleFactorOperation->getOutputSocket(), operation->getInputSocket(2));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
+
+ operation->setVariableSize(inputXSocket->isLinked() ||
+ inputYSocket->isLinked());
+
break;
}
case CMP_SCALE_RENDERPERCENT:
@@ -81,9 +88,13 @@ void ScaleNode::convertToOperations(NodeConverter &converter, const CompositorCo
operation->setNewHeight(rd->ysch * rd->size / 100.0f);
operation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
converter.addOperation(operation);
-
+
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
+
+ operation->setVariableSize(inputXSocket->isLinked() ||
+ inputYSocket->isLinked());
+
break;
}
case CMP_SCALE_ABSOLUTE:
@@ -91,11 +102,15 @@ void ScaleNode::convertToOperations(NodeConverter &converter, const CompositorCo
/* TODO: what is the use of this one.... perhaps some issues when the ui was updated... */
ScaleAbsoluteOperation *operation = new ScaleAbsoluteOperation();
converter.addOperation(operation);
-
+
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapInputSocket(inputXSocket, operation->getInputSocket(1));
converter.mapInputSocket(inputYSocket, operation->getInputSocket(2));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
+
+ operation->setVariableSize(inputXSocket->isLinked() ||
+ inputYSocket->isLinked());
+
break;
}
}
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cpp b/source/blender/compositor/operations/COM_ScaleOperation.cpp
index 46e155e43b5..b498b359144 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cpp
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cpp
@@ -36,6 +36,7 @@ BaseScaleOperation::BaseScaleOperation()
#else
m_sampler = -1;
#endif
+ m_variable_size = false;
}
ScaleOperation::ScaleOperation() : BaseScaleOperation()
@@ -87,20 +88,27 @@ void ScaleOperation::executePixelSampled(float output[4], float x, float y, Pixe
bool ScaleOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
rcti newInput;
- float scaleX[4];
- float scaleY[4];
-
- this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
- this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
+ if (!m_variable_size) {
+ float scaleX[4];
+ float scaleY[4];
- const float scx = scaleX[0];
- const float scy = scaleY[0];
+ this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
+ this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
- newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / scx;
- newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / scx;
- newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / scy;
- newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / scy;
+ const float scx = scaleX[0];
+ const float scy = scaleY[0];
+ newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / scx;
+ newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / scx;
+ newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / scy;
+ newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / scy;
+ }
+ else {
+ newInput.xmax = this->getWidth();
+ newInput.xmin = 0;
+ newInput.ymax = this->getHeight();
+ newInput.ymin = 0;
+ }
return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
@@ -162,24 +170,32 @@ void ScaleAbsoluteOperation::executePixelSampled(float output[4], float x, float
bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
rcti newInput;
- float scaleX[4];
- float scaleY[4];
-
- this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
- this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
-
- const float scx = scaleX[0];
- const float scy = scaleY[0];
- const float width = this->getWidth();
- const float height = this->getHeight();
- //div
- float relateveXScale = scx / width;
- float relateveYScale = scy / height;
-
- newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / relateveXScale;
- newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / relateveXScale;
- newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / relateveYScale;
- newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / relateveYScale;
+ if (!m_variable_size) {
+ float scaleX[4];
+ float scaleY[4];
+
+ this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
+ this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
+
+ const float scx = scaleX[0];
+ const float scy = scaleY[0];
+ const float width = this->getWidth();
+ const float height = this->getHeight();
+ //div
+ float relateveXScale = scx / width;
+ float relateveYScale = scy / height;
+
+ newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / relateveXScale;
+ newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / relateveXScale;
+ newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / relateveYScale;
+ newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / relateveYScale;
+ }
+ else {
+ newInput.xmax = this->getWidth();
+ newInput.xmin = 0;
+ newInput.ymax = this->getHeight();
+ newInput.ymin = 0;
+ }
return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 706a5898027..17cd31f9710 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -28,6 +28,7 @@
class BaseScaleOperation : public NodeOperation {
public:
void setSampler(PixelSampler sampler) { this->m_sampler = (int) sampler; }
+ void setVariableSize(bool variable_size) { m_variable_size = variable_size; };
protected:
BaseScaleOperation();
@@ -35,6 +36,7 @@ protected:
PixelSampler getEffectiveSampler(PixelSampler sampler) { return (m_sampler == -1) ? sampler : (PixelSampler) m_sampler; }
int m_sampler;
+ bool m_variable_size;
};
class ScaleOperation : public BaseScaleOperation {
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index 8fddf2551b8..5261baeab57 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -454,15 +454,6 @@ int join_mesh_exec(bContext *C, wmOperator *op)
/* adjust settings to fit (allocate a new data-array) */
kbn->data = MEM_callocN(sizeof(float) * 3 * totvert, "joined_shapekey");
kbn->totelem = totvert;
-
- /* XXX 2.5 Animato */
-#if 0
- /* also, copy corresponding ipo-curve to ipo-block if applicable */
- if (me->key->ipo && key->ipo) {
- /* FIXME... this is a luxury item! */
- puts("FIXME: ignoring IPO's when joining shapekeys on Meshes for now...");
- }
-#endif
}
kb_map[i] = kbn;
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 9775c136586..1df6030bb10 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -89,10 +89,76 @@ struct WPaintAverageAccum {
double value;
};
-static void defweight_prev_init(const MDeformWeight *dw, float *weight_prev)
+struct NormalAnglePrecalc {
+ bool do_mask_normal;
+ /* what angle to mask at */
+ float angle;
+ /* cos(angle), faster to compare */
+ float angle__cos;
+ float angle_inner;
+ float angle_inner__cos;
+ /* difference between angle and angle_inner, for easy access */
+ float angle_range;
+};
+
+
+static void view_angle_limits_init(
+ struct NormalAnglePrecalc *a, float angle, bool do_mask_normal)
+{
+ a->do_mask_normal = do_mask_normal;
+ if (do_mask_normal) {
+ a->angle_inner = angle;
+ a->angle = (a->angle_inner + 90.0f) * 0.5f;
+ }
+ else {
+ a->angle_inner = a->angle = angle;
+ }
+
+ a->angle_inner *= (float)(M_PI_2 / 90);
+ a->angle *= (float)(M_PI_2 / 90);
+ a->angle_range = a->angle - a->angle_inner;
+
+ if (a->angle_range <= 0.0f) {
+ a->do_mask_normal = false; /* no need to do blending */
+ }
+
+ a->angle__cos = cosf(a->angle);
+ a->angle_inner__cos = cosf(a->angle_inner);
+}
+
+static float view_angle_limits_apply_falloff(
+ const struct NormalAnglePrecalc *a, float angle_cos, float *mask_p)
{
- if (UNLIKELY(*weight_prev == -1.0f)) {
- *weight_prev = dw ? dw->weight : 0.0f;
+ if (angle_cos <= a->angle__cos) {
+ /* outsize the normal limit */
+ return false;
+ }
+ else if (angle_cos < a->angle_inner__cos) {
+ *mask_p *= (a->angle - acosf(angle_cos)) / a->angle_range;
+ return true;
+ }
+ else {
+ return true;
+ }
+}
+
+static bool vwpaint_use_normal(const VPaint *vp)
+{
+ return ((vp->flag & VP_FLAG_PROJECT_BACKFACE) == 0) ||
+ ((vp->flag & VP_FLAG_PROJECT_FLAT) == 0);
+}
+
+
+static void defweight_prev_restore_or_init(MDeformVert *dvert_prev, MDeformVert *dvert_curr, int index)
+{
+ MDeformVert *dv_curr = &dvert_curr[index];
+ MDeformVert *dv_prev = &dvert_prev[index];
+ if (dv_prev->flag == 1) {
+ dv_prev->flag = 0;
+ defvert_copy(dv_prev, dv_curr);
+ }
+ else {
+ defvert_copy(dv_curr, dv_prev);
}
}
@@ -137,7 +203,7 @@ int vertex_paint_mode_poll(bContext *C)
int vertex_paint_poll(bContext *C)
{
- if (vertex_paint_mode_poll(C) &&
+ if (vertex_paint_mode_poll(C) &&
BKE_paint_brush(&CTX_data_tool_settings(C)->vpaint->paint))
{
ScrArea *sa = CTX_wm_area(C);
@@ -179,8 +245,8 @@ int weight_paint_poll(bContext *C)
static VPaint *new_vpaint(int wpaint)
{
VPaint *vp = MEM_callocN(sizeof(VPaint), "VPaint");
-
- vp->flag = (wpaint) ? 0 : VP_SPRAY;
+
+ vp->flag = (wpaint) ? 0 : VP_FLAG_SPRAY;
vp->paint.flags |= PAINT_SHOW_BRUSH;
return vp;
@@ -208,7 +274,7 @@ static uint vpaint_blend(
uint color_blend = ED_vpaint_blend_tool(tool, color_curr, color_paint, alpha_i);
/* if no spray, clip color adding with colorig & orig alpha */
- if ((vp->flag & VP_SPRAY) == 0) {
+ if ((vp->flag & VP_FLAG_SPRAY) == 0) {
uint color_test, a;
char *cp, *ct, *co;
@@ -250,9 +316,10 @@ static float calc_vp_strength_col_dl(
{
float co_ss[2]; /* screenspace */
- if (ED_view3d_project_float_object(vc->ar,
- co, co_ss,
- V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK)
+ if (ED_view3d_project_float_object(
+ vc->ar,
+ co, co_ss,
+ V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK)
{
const float dist_sq = len_squared_v2v2(mval, co_ss);
@@ -293,7 +360,7 @@ static float calc_vp_alpha_col_dl(
if (strength > 0.0f) {
float alpha = brush_alpha_pressure * strength;
- if (vp->flag & VP_NORMALS) {
+ if ((vp->flag & VP_FLAG_PROJECT_FLAT) == 0) {
float dvec[3];
/* transpose ! */
@@ -316,10 +383,11 @@ static float calc_vp_alpha_col_dl(
}
/* vpaint has 'vpaint_blend' */
-static float wpaint_blend(VPaint *wp, float weight,
- const float alpha, float paintval,
- const float UNUSED(brush_alpha_value),
- const short do_flip)
+static float wpaint_blend(
+ VPaint *wp, float weight,
+ const float alpha, float paintval,
+ const float UNUSED(brush_alpha_value),
+ const short do_flip)
{
Brush *brush = BKE_paint_brush(&wp->paint);
int tool = brush->vertexpaint_tool;
@@ -338,11 +406,11 @@ static float wpaint_blend(VPaint *wp, float weight,
tool = PAINT_BLEND_LIGHTEN; break;
}
}
-
+
weight = ED_wpaint_blend_tool(tool, weight, paintval, alpha);
CLAMP(weight, 0.0f, 1.0f);
-
+
return weight;
}
@@ -497,8 +565,9 @@ static void do_weight_paint_normalize_all_locked_try_active(
}
#if 0 /* UNUSED */
-static bool has_unselected_unlocked_bone_group(int defbase_tot, bool *defbase_sel, int selected,
- const bool *lock_flags, const bool *vgroup_validmap)
+static bool has_unselected_unlocked_bone_group(
+ int defbase_tot, bool *defbase_sel, int selected,
+ const bool *lock_flags, const bool *vgroup_validmap)
{
int i;
if (defbase_tot == selected) {
@@ -611,13 +680,15 @@ typedef struct WeightPaintInfo {
struct WeightPaintGroupData active, mirror;
- const bool *lock_flags; /* boolean array for locked bones,
- * length of defbase_tot */
- const bool *defbase_sel; /* boolean array for selected bones,
- * length of defbase_tot, cant be const because of how its passed */
-
- const bool *vgroup_validmap; /* same as WeightPaintData.vgroup_validmap,
- * only added here for convenience */
+ /* boolean array for locked bones,
+ * length of defbase_tot */
+ const bool *lock_flags;
+ /* boolean array for selected bones,
+ * length of defbase_tot, cant be const because of how its passed */
+ const bool *defbase_sel;
+ /* same as WeightPaintData.vgroup_validmap,
+ * only added here for convenience */
+ const bool *vgroup_validmap;
bool do_flip;
bool do_multipaint;
@@ -630,8 +701,7 @@ static void do_weight_paint_vertex_single(
/* vars which remain the same for every vert */
VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
/* vars which change on each stroke */
- const uint index, float alpha, float paintweight
- )
+ const uint index, float alpha, float paintweight)
{
Mesh *me = ob->data;
MDeformVert *dv = &me->dvert[index];
@@ -646,18 +716,6 @@ static void do_weight_paint_vertex_single(
MDeformVert *dv_mirr;
MDeformWeight *dw_mirr;
- if (wp->flag & VP_ONLYVGROUP) {
- dw = defvert_find_index(dv, wpi->active.index);
- }
- else {
- dw = defvert_verify_index(dv, wpi->active.index);
- }
-
- if (dw == NULL) {
- return;
- }
-
-
/* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */
if (me->editflag & ME_EDIT_MIRROR_X) {
index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology);
@@ -673,11 +731,29 @@ static void do_weight_paint_vertex_single(
index_mirr = vgroup_mirr = -1;
}
+ if ((wp->flag & VP_FLAG_SPRAY) == 0) {
+ struct MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
+ defweight_prev_restore_or_init(dvert_prev, me->dvert, index);
+ if (index_mirr != -1) {
+ defweight_prev_restore_or_init(dvert_prev, me->dvert, index_mirr);
+ }
+ }
+
+ if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
+ dw = defvert_find_index(dv, wpi->active.index);
+ }
+ else {
+ dw = defvert_verify_index(dv, wpi->active.index);
+ }
+
+ if (dw == NULL) {
+ return;
+ }
/* get the mirror def vars */
if (index_mirr != -1) {
dv_mirr = &me->dvert[index_mirr];
- if (wp->flag & VP_ONLYVGROUP) {
+ if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr);
if (dw_mirr == NULL) {
@@ -711,8 +787,9 @@ static void do_weight_paint_vertex_single(
* then there is no need to run the more complicated checks */
{
- dw->weight = wpaint_blend(wp, dw->weight, alpha, paintweight,
- wpi->brush_alpha_value, wpi->do_flip);
+ dw->weight = wpaint_blend(
+ wp, dw->weight, alpha, paintweight,
+ wpi->brush_alpha_value, wpi->do_flip);
/* WATCH IT: take care of the ordering of applying mirror -> normalize,
* can give wrong results [#26193], least confusing if normalize is done last */
@@ -793,6 +870,17 @@ static void do_weight_paint_vertex_multi(
if (index_mirr != -1 && index_mirr != index) {
dv_mirr = &me->dvert[index_mirr];
}
+ else {
+ index_mirr = -1;
+ }
+ }
+
+ if ((wp->flag & VP_FLAG_SPRAY) == 0) {
+ struct MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
+ defweight_prev_restore_or_init(dvert_prev, me->dvert, index);
+ if (index_mirr != -1) {
+ defweight_prev_restore_or_init(dvert_prev, me->dvert, index_mirr);
+ }
}
/* compute weight change by applying the brush to average or sum of group weights */
@@ -920,7 +1008,7 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
/* Create average brush arrays */
if (ob->mode == OB_MODE_VERTEX_PAINT) {
- if ((ts->vpaint->flag & VP_SPRAY) == 0) {
+ if ((ts->vpaint->flag & VP_FLAG_SPRAY) == 0) {
if (ob->sculpt->mode.vpaint.previous_color == NULL) {
ob->sculpt->mode.vpaint.previous_color =
MEM_callocN(me->totloop * sizeof(uint), __func__);
@@ -941,18 +1029,28 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
}
}
else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
- if ((ts->wpaint->flag & VP_SPRAY) == 0) {
+ if ((ts->wpaint->flag & VP_FLAG_SPRAY) == 0) {
if (ob->sculpt->mode.wpaint.alpha_weight == NULL) {
ob->sculpt->mode.wpaint.alpha_weight =
MEM_callocN(me->totvert * sizeof(float), __func__);
}
- if (ob->sculpt->mode.wpaint.previous_weight == NULL) {
- ob->sculpt->mode.wpaint.previous_weight =
- MEM_mallocN(me->totvert * sizeof(float), __func__);
+ if (ob->sculpt->mode.wpaint.dvert_prev == NULL) {
+ ob->sculpt->mode.wpaint.dvert_prev =
+ MEM_callocN(me->totvert * sizeof(MDeformVert), __func__);
+ MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev;
+ for (int i = 0; i < me->totvert; i++, dv++) {
+ /* Use to show this isn't initialized, never apply to the mesh data. */
+ dv->flag = 1;
+ }
}
}
else {
MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight);
+ if (ob->sculpt->mode.wpaint.dvert_prev != NULL) {
+ BKE_defvert_array_free_elems(ob->sculpt->mode.wpaint.dvert_prev, me->totvert);
+ MEM_freeN(ob->sculpt->mode.wpaint.dvert_prev);
+ ob->sculpt->mode.wpaint.dvert_prev = NULL;
+ }
}
if (brush && brush->flag & BRUSH_ACCUMULATE) {
if (ob->sculpt->mode.wpaint.previous_accum == NULL) {
@@ -973,7 +1071,7 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
* \note Keep in sync with #vpaint_mode_toggle_exec
*/
static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
-{
+{
Object *ob = CTX_data_active_object(C);
const int mode_flag = OB_MODE_WEIGHT_PAINT;
const bool is_mode_set = (ob->mode & mode_flag) != 0;
@@ -1039,7 +1137,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
}
BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
-
+
/* Weightpaint works by overriding colors in mesh,
* so need to make sure we recalc on enter and
* exit (exit needs doing regardless because we
@@ -1067,25 +1165,25 @@ static int paint_poll_test(bContext *C)
void PAINT_OT_weight_paint_toggle(wmOperatorType *ot)
{
-
+
/* identifiers */
ot->name = "Weight Paint Mode";
ot->idname = "PAINT_OT_weight_paint_toggle";
ot->description = "Toggle weight paint mode in 3D view";
-
+
/* api callbacks */
ot->exec = wpaint_mode_toggle_exec;
ot->poll = paint_poll_test;
-
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
}
/* ************ weight paint operator ********** */
struct WPaintData {
ViewContext vc;
+ struct NormalAnglePrecalc normal_angle_precalc;
struct WeightPaintGroupData active, mirror;
@@ -1108,12 +1206,12 @@ struct WPaintData {
/* Initialize the stroke cache invariants from operator properties */
static void vwpaint_update_cache_invariants(
- bContext *C, VPaint *vd, SculptSession *ss, wmOperator *op, const float mouse[2])
+ bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2])
{
StrokeCache *cache;
Scene *scene = CTX_data_scene(C);
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
- Brush *brush = BKE_paint_brush(&vd->paint);
+ Brush *brush = BKE_paint_brush(&vp->paint);
ViewContext *vc = paint_stroke_view_context(op->customdata);
Object *ob = CTX_data_active_object(C);
float mat[3][3];
@@ -1165,12 +1263,12 @@ static void vwpaint_update_cache_invariants(
}
/* Initialize the stroke cache variants from operator properties */
-static void vwpaint_update_cache_variants(bContext *C, VPaint *vd, Object *ob, PointerRNA *ptr)
+static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, PointerRNA *ptr)
{
Scene *scene = CTX_data_scene(C);
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
- Brush *brush = BKE_paint_brush(&vd->paint);
+ Brush *brush = BKE_paint_brush(&vp->paint);
/* This effects the actual brush radius, so things farther away
* are compared with a larger radius and vise versa. */
@@ -1228,7 +1326,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
int defbase_tot, defbase_tot_sel;
bool *defbase_sel;
SculptSession *ss = ob->sculpt;
- VPaint *vd = CTX_data_tool_settings(C)->wpaint;
+ VPaint *vp = CTX_data_tool_settings(C)->wpaint;
EvaluationContext eval_ctx;
float mat[4][4], imat[4][4];
@@ -1286,6 +1384,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData");
paint_stroke_set_mode_data(stroke, wpd);
view3d_set_viewcontext(C, &wpd->vc);
+ view_angle_limits_init(&wpd->normal_angle_precalc, vp->normal_angle, (vp->flag & VP_FLAG_PROJECT_FLAT) == 0);
wpd->active.index = vgroup_index.active;
wpd->mirror.index = vgroup_index.mirror;
@@ -1342,11 +1441,15 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
/* If not previously created, create vertex/weight paint mode session data */
vertex_paint_init_session(&eval_ctx, scene, ob);
- vwpaint_update_cache_invariants(C, vd, ss, op, mouse);
+ vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
vertex_paint_init_session_data(ts, ob);
- if (ss->mode.wpaint.previous_weight != NULL) {
- copy_vn_fl(ss->mode.wpaint.previous_weight, me->totvert, -1.0f);
+ if (ob->sculpt->mode.wpaint.dvert_prev != NULL) {
+ MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev;
+ for (int i = 0; i < me->totvert; i++, dv++) {
+ /* Use to show this isn't initialized, never apply to the mesh data. */
+ dv->flag = 1;
+ }
}
return true;
@@ -1360,7 +1463,7 @@ static float dot_vf3vs3(const float brushNormal[3], const short vertexNormal[3])
}
static void get_brush_alpha_data(
- Scene *scene, SculptSession *ss, Brush *brush,
+ const Scene *scene, const SculptSession *ss, const Brush *brush,
float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure)
{
*r_brush_size_pressure =
@@ -1372,6 +1475,36 @@ static void get_brush_alpha_data(
(BKE_brush_use_alpha_pressure(scene, brush) ? ss->cache->pressure : 1.0f);
}
+static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintInfo *wpi)
+{
+ if (wpi->do_multipaint) {
+ float weight = BKE_defvert_multipaint_collective_weight(
+ dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
+
+ CLAMP(weight, 0.0f, 1.0f);
+ return weight;
+ }
+ else {
+ return defvert_find_weight(dv, wpi->active.index);
+ }
+}
+
+static SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(
+ SculptSession *ss, SculptBrushTest *test, char falloff_shape)
+{
+ sculpt_brush_test_init(ss, test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn;
+ if (falloff_shape == VP_FALLOFF_SHAPE_SPHERE) {
+ sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq;
+ }
+ else {
+ /* VP_FALLOFF_SHAPE_TUBE */
+ plane_from_point_normal_v3(test->plane, test->location, ss->cache->view_normal);
+ sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq;
+ }
+ return sculpt_brush_test_sq_fn;
+}
+
static void do_wpaint_brush_blur_task_cb_ex(
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
{
@@ -1380,25 +1513,26 @@ static void do_wpaint_brush_blur_task_cb_ex(
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
- Brush *brush = data->brush;
- StrokeCache *cache = ss->cache;
+ const Brush *brush = data->brush;
+ const StrokeCache *cache = ss->cache;
Scene *scene = CTX_data_scene(data->C);
- const float brush_strength = cache->bstrength;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
+ const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop coopresponds to the current grid.
* Otherwise, take the current vert. */
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
@@ -1418,17 +1552,23 @@ static void do_wpaint_brush_blur_task_cb_ex(
const int l_index = mp->loopstart + k;
const MLoop *ml = &data->me->mloop[l_index];
const MDeformVert *dv = &data->me->dvert[ml->v];
- weight_final += defvert_find_weight(dv, data->wpi->active.index);
+ weight_final += wpaint_get_active_weight(dv, data->wpi);
}
}
/* Apply the weight to the vertex. */
if (total_hit_loops != 0) {
- const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
- if (view_dot > 0.0f) {
+ float brush_strength = cache->bstrength;
+ const float angle_cos = (use_normal && vd.no) ?
+ dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
+ if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
+ (angle_cos > 0.0f)) &&
+ ((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
+ view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength)))
+ {
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
float final_alpha =
- view_dot * brush_fade * brush_strength *
+ brush_fade * brush_strength *
grid_alpha * brush_alpha_pressure;
if (brush->flag & BRUSH_ACCUMULATE) {
@@ -1458,12 +1598,12 @@ static void do_wpaint_brush_smear_task_cb_ex(
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
- Brush *brush = data->brush;
- Scene *scene = CTX_data_scene(data->C);
- StrokeCache *cache = ss->cache;
- const float brush_strength = cache->bstrength;
+ const Brush *brush = data->brush;
+ const Scene *scene = CTX_data_scene(data->C);
+ const StrokeCache *cache = ss->cache;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
+ const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
float brush_dir[3];
@@ -1474,26 +1614,32 @@ static void do_wpaint_brush_smear_task_cb_ex(
if (normalize_v3(brush_dir) != 0.0f) {
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test_fast(&test, vd.co)) {
- const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
- if (view_dot > 0.0f) {
- bool do_color = false;
-
- /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
- * Otherwise, take the current vert. */
- const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
- const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
- const MVert *mv_curr = &data->me->mvert[v_index];
-
- /* If the vertex is selected */
- if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
+ * Otherwise, take the current vert. */
+ const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
+ const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
+ const MVert *mv_curr = &data->me->mvert[v_index];
+
+ /* If the vertex is selected */
+ if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) {
+ float brush_strength = cache->bstrength;
+ const float angle_cos = (use_normal && vd.no) ?
+ dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
+ if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
+ (angle_cos > 0.0f)) &&
+ ((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
+ view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength)))
+ {
+ bool do_color = false;
/* Minimum dot product between brush direction and current
* to neighbor direction is 0.0, meaning orthogonal. */
float stroke_dot_max = 0.0f;
@@ -1522,7 +1668,7 @@ static void do_wpaint_brush_smear_task_cb_ex(
if (stroke_dot > stroke_dot_max) {
stroke_dot_max = stroke_dot;
MDeformVert *dv = &data->me->dvert[v_other_index];
- weight_final = defvert_find_weight(dv, data->wpi->active.index);
+ weight_final = wpaint_get_active_weight(dv, data->wpi);
do_color = true;
}
}
@@ -1530,9 +1676,9 @@ static void do_wpaint_brush_smear_task_cb_ex(
}
/* Apply weight to vertex */
if (do_color) {
- const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
- const float final_alpha =
- view_dot * brush_fade * brush_strength *
+ const float brush_fade = BKE_brush_curve_strength(brush, 0.0f, cache->radius);
+ float final_alpha =
+ brush_fade * brush_strength *
grid_alpha * brush_alpha_pressure;
do_weight_paint_vertex(
data->vp, data->ob, data->wpi,
@@ -1552,26 +1698,27 @@ static void do_wpaint_brush_draw_task_cb_ex(
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
- Scene *scene = CTX_data_scene(data->C);
+ const Scene *scene = CTX_data_scene(data->C);
- Brush *brush = data->brush;
- StrokeCache *cache = ss->cache;
- const float brush_strength = cache->bstrength;
+ const Brush *brush = data->brush;
+ const StrokeCache *cache = ss->cache;
const float paintweight = BKE_brush_weight_get(scene, brush);
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
+ const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* Note: grids are 1:1 with corners (aka loops).
* For multires, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */
@@ -1581,11 +1728,16 @@ static void do_wpaint_brush_draw_task_cb_ex(
const char v_flag = data->me->mvert[v_index].flag;
/* If the vertex is selected */
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
- const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
- if (view_dot > 0.0f) {
+ float brush_strength = cache->bstrength;
+ const float angle_cos = (use_normal && vd.no) ?
+ dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
+ if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
+ (angle_cos > 0.0f)) &&
+ ((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
+ view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength)))
+ {
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
- float final_alpha = view_dot * brush_fade * brush_strength * grid_alpha * brush_alpha_pressure;
-
+ float final_alpha = brush_fade * brush_strength * grid_alpha * brush_alpha_pressure;
if (brush->flag & BRUSH_ACCUMULATE) {
float mask_accum = ss->mode.wpaint.previous_accum[v_index];
final_alpha = min_ff(final_alpha + mask_accum, brush_strength);
@@ -1593,7 +1745,7 @@ static void do_wpaint_brush_draw_task_cb_ex(
}
/* Non-spray logic. */
- if ((data->vp->flag & VP_SPRAY) == 0) {
+ if ((data->vp->flag & VP_FLAG_SPRAY) == 0) {
/* Only paint if we have greater alpha. */
if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) {
ss->mode.wpaint.alpha_weight[v_index] = final_alpha;
@@ -1601,14 +1753,6 @@ static void do_wpaint_brush_draw_task_cb_ex(
else {
continue;
}
-
- MDeformVert *dv = &data->me->dvert[v_index];
- MDeformWeight *dw = defvert_find_index(dv, data->wpi->active.index);
- float *weight_prev = &ss->mode.wpaint.previous_weight[v_index];
- defweight_prev_init(dw, weight_prev);
- if (dw) {
- dw->weight = *weight_prev;
- }
}
do_weight_paint_vertex(
@@ -1629,6 +1773,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
StrokeCache *cache = ss->cache;
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+ const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
@@ -1637,16 +1782,18 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
accum->value = 0.0;
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test_sq(&test, vd.co)) {
- const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
- if (view_dot > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float angle_cos = (use_normal && vd.no) ?
+ dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
+ if (angle_cos > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) {
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
// const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
const char v_flag = data->me->mvert[v_index].flag;
@@ -1655,7 +1802,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
const MDeformVert *dv = &data->me->dvert[v_index];
accum->len += 1;
- accum->value += defvert_find_weight(dv, data->wpi->active.index);
+ accum->value += wpaint_get_active_weight(dv, data->wpi);
}
}
}
@@ -1704,31 +1851,78 @@ static void wpaint_paint_leaves(
.sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .vp = vp, .wpd = wpd, .wpi = wpi, .me = me, .C = C,
};
+ /* current mirroring code cannot be run in parallel */
+ bool use_threading = !(me->editflag & ME_EDIT_MIRROR_X);
+
switch (brush->vertexpaint_tool) {
case PAINT_BLEND_AVERAGE:
calculate_average_weight(&data, nodes, totnode);
BLI_task_parallel_range_ex(
0, totnode, &data, NULL, 0,
- do_wpaint_brush_draw_task_cb_ex, true, false);
+ do_wpaint_brush_draw_task_cb_ex, use_threading, false);
break;
case PAINT_BLEND_SMEAR:
BLI_task_parallel_range_ex(
0, totnode, &data, NULL, 0,
- do_wpaint_brush_smear_task_cb_ex, true, false);
+ do_wpaint_brush_smear_task_cb_ex, use_threading, false);
break;
case PAINT_BLEND_BLUR:
BLI_task_parallel_range_ex(
0, totnode, &data, NULL, 0,
- do_wpaint_brush_blur_task_cb_ex, true, false);
+ do_wpaint_brush_blur_task_cb_ex, use_threading, false);
break;
default:
BLI_task_parallel_range_ex(
0, totnode, &data, NULL, 0,
- do_wpaint_brush_draw_task_cb_ex, true, false);
+ do_wpaint_brush_draw_task_cb_ex, use_threading, false);
break;
}
}
+static PBVHNode **vwpaint_pbvh_gather_generic(
+ Object *ob, VPaint *wp, Sculpt *sd, Brush *brush, int *r_totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ const bool use_normal = vwpaint_use_normal(wp);
+ PBVHNode **nodes = NULL;
+
+ /* Build a list of all nodes that are potentially within the brush's area of influence */
+ if (wp->falloff_shape == VP_FALLOFF_SHAPE_SPHERE) {
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .sd = sd,
+ .radius_squared = ss->cache->radius_squared,
+ .original = true,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode);
+ if (use_normal) {
+ sculpt_pbvh_calc_area_normal(brush, ob, nodes, *r_totnode, true, ss->cache->sculpt_normal_symm);
+ }
+ else {
+ zero_v3(ss->cache->sculpt_normal_symm);
+ }
+ }
+ else {
+ struct DistRayAABB_Precalc dist_ray_to_aabb_precalc;
+ dist_squared_ray_to_aabb_precalc(&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal);
+ SculptSearchCircleData data = {
+ .ss = ss,
+ .sd = sd,
+ .radius_squared = ss->cache->radius_squared,
+ .original = true,
+ .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode);
+ if (use_normal) {
+ copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal);
+ }
+ else {
+ zero_v3(ss->cache->sculpt_normal_symm);
+ }
+ }
+ return nodes;
+}
+
static void wpaint_do_paint(
bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi,
Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle)
@@ -1737,19 +1931,9 @@ static void wpaint_do_paint(
ss->cache->radial_symmetry_pass = i;
sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle);
- SculptSearchSphereData data;
- PBVHNode **nodes = NULL;
int totnode;
+ PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, wp, sd, brush, &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 = true;
- BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
-
- sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, ss->cache->sculpt_normal_symm);
wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode);
if (nodes)
@@ -1843,7 +2027,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
vc = &wpd->vc;
ob = vc->obact;
-
+
view3d_operator_needs_opengl(C);
ED_view3d_init_mats_rv3d(ob, vc->rv3d);
@@ -1908,7 +2092,7 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
{
Object *ob = CTX_data_active_object(C);
struct WPaintData *wpd = paint_stroke_mode_data(stroke);
-
+
if (wpd) {
ED_vpaint_proj_handle_free(wpd->vp_handle);
@@ -1925,12 +2109,12 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
MEM_freeN(wpd);
}
-
+
/* and particles too */
if (ob->particlesystem.first) {
ParticleSystem *psys;
int i;
-
+
for (psys = ob->particlesystem.first; psys; psys = psys->next) {
for (i = 0; i < PSYS_TOT_VG; i++) {
if (psys->vgroup[i] == ob->actdef) {
@@ -1958,7 +2142,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
C, op, sculpt_stroke_get_location, wpaint_stroke_test_start,
wpaint_stroke_update_step, NULL,
wpaint_stroke_done, event->type);
-
+
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
paint_stroke_data_free(op);
return OPERATOR_FINISHED;
@@ -1968,7 +2152,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
OPERATOR_RETVAL_CHECK(retval);
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
-
+
return OPERATOR_RUNNING_MODAL;
}
@@ -1998,22 +2182,21 @@ static void wpaint_cancel(bContext *C, wmOperator *op)
void PAINT_OT_weight_paint(wmOperatorType *ot)
{
-
/* identifiers */
ot->name = "Weight Paint";
ot->idname = "PAINT_OT_weight_paint";
ot->description = "Paint a stroke in the current vertex group's weights";
-
+
/* api callbacks */
ot->invoke = wpaint_invoke;
ot->modal = paint_stroke_modal;
ot->exec = wpaint_exec;
ot->poll = weight_paint_poll;
ot->cancel = wpaint_cancel;
-
+
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
-
+
paint_stroke_operator_properties(ot);
}
@@ -2023,7 +2206,7 @@ void PAINT_OT_weight_paint(wmOperatorType *ot)
* \note Keep in sync with #wpaint_mode_toggle_exec
*/
static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
-{
+{
Object *ob = CTX_data_active_object(C);
const int mode_flag = OB_MODE_VERTEX_PAINT;
const bool is_mode_set = (ob->mode & mode_flag) != 0;
@@ -2038,7 +2221,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
}
me = BKE_mesh_from_object(ob);
-
+
/* toggle: end vpaint */
if (is_mode_set) {
ob->mode &= ~mode_flag;
@@ -2071,7 +2254,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
if (vp == NULL)
vp = scene->toolsettings->vpaint = new_vpaint(0);
-
+
paint_cursor_start(C, vertex_paint_poll);
BKE_paint_init(scene, ePaintVertex, PAINT_CURSOR_VERTEX_PAINT);
@@ -2091,24 +2274,23 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
/* update modifier stack for mapping requirements */
DEG_id_tag_update(&me->id, 0);
-
+
WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
-
+
return OPERATOR_FINISHED;
}
void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
{
-
/* identifiers */
ot->name = "Vertex Paint Mode";
ot->idname = "PAINT_OT_vertex_paint_toggle";
ot->description = "Toggle the vertex paint mode in 3D view";
-
+
/* api callbacks */
ot->exec = vpaint_mode_toggle_exec;
ot->poll = paint_poll_test;
-
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
@@ -2123,7 +2305,7 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
* - validate context (add mcol)
* - create customdata storage
* - call paint once (mouse click)
- * - add modal handler
+ * - add modal handler
*
* Operator->modal()
* - for every mousemove, apply vertex paint
@@ -2142,6 +2324,8 @@ typedef struct PolyFaceMap {
struct VPaintData {
ViewContext vc;
+ struct NormalAnglePrecalc normal_angle_precalc;
+
uint paintcol;
struct VertProjHandle *vp_handle;
@@ -2180,7 +2364,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
me = BKE_mesh_from_object(ob);
if (me == NULL || me->totpoly == 0)
return false;
-
+
ED_mesh_color_ensure(me, NULL);
if (me->mloopcol == NULL)
return false;
@@ -2189,7 +2373,8 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
vpd = MEM_callocN(sizeof(*vpd), "VPaintData");
paint_stroke_set_mode_data(stroke, vpd);
view3d_set_viewcontext(C, &vpd->vc);
-
+ view_angle_limits_init(&vpd->normal_angle_precalc, vp->normal_angle, (vp->flag & VP_FLAG_PROJECT_FLAT) == 0);
+
vpd->paintcol = vpaint_get_current_col(scene, vp);
vpd->is_texbrush = !(brush->vertexpaint_tool == PAINT_BLEND_BLUR) &&
@@ -2253,16 +2438,17 @@ static void do_vpaint_brush_calc_average_color_cb_ex(
memset(accum->value, 0, sizeof(accum->value));
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test_fast(&test, vd.co)) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
- if (BKE_brush_curve_strength(data->brush, test.dist, cache->radius) > 0.0) {
+ if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) {
/* If the vertex is selected for painting. */
const MVert *mv = &data->me->mvert[v_index];
if (!use_vert_sel || mv->flag & SELECT) {
@@ -2310,25 +2496,26 @@ static void do_vpaint_brush_draw_task_cb_ex(
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
- Brush *brush = data->brush;
- StrokeCache *cache = ss->cache;
- const float brush_strength = cache->bstrength;
+ const Brush *brush = data->brush;
+ const StrokeCache *cache = ss->cache;
uint *lcol = data->lcol;
- Scene *scene = CTX_data_scene(data->C);
+ const Scene *scene = CTX_data_scene(data->C);
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
+ const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* Note: Grids are 1:1 with corners (aka loops).
* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */
@@ -2340,9 +2527,15 @@ static void do_vpaint_brush_draw_task_cb_ex(
if (!use_vert_sel || mv->flag & SELECT) {
/* Calc the dot prod. between ray norm on surf and current vert
* (ie splash prevention factor), and only paint front facing verts. */
- const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
- if (view_dot > 0.0f) {
- const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
+ float brush_strength = cache->bstrength;
+ const float angle_cos = (use_normal && vd.no) ?
+ dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
+ if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
+ (angle_cos > 0.0f)) &&
+ ((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
+ view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength)))
+ {
+ const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
uint color_final = data->vpd->paintcol;
/* If we're painting with a texture, sample the texture color and alpha. */
@@ -2368,9 +2561,8 @@ static void do_vpaint_brush_draw_task_cb_ex(
color_orig = ss->mode.vpaint.previous_color[l_index];
}
float final_alpha =
- 255 * brush_fade * brush_strength * view_dot *
+ 255 * brush_fade * brush_strength *
tex_alpha * brush_alpha_pressure * grid_alpha;
-
if (brush->flag & BRUSH_ACCUMULATE) {
float mask_accum = ss->mode.vpaint.previous_accum[l_index];
final_alpha = min_ff(final_alpha + mask_accum, 255.0f * brush_strength);
@@ -2399,36 +2591,43 @@ static void do_vpaint_brush_blur_task_cb_ex(
Scene *scene = CTX_data_scene(data->C);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
- Brush *brush = data->brush;
- StrokeCache *cache = ss->cache;
- const float brush_strength = cache->bstrength;
+ const Brush *brush = data->brush;
+ const StrokeCache *cache = ss->cache;
uint *lcol = data->lcol;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
+ const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test(&test, vd.co)) {
- /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
const MVert *mv = &data->me->mvert[v_index];
- const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
- if (view_dot > 0.0f) {
- const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
+ /* If the vertex is selected for painting. */
+ if (!use_vert_sel || mv->flag & SELECT) {
+ float brush_strength = cache->bstrength;
+ const float angle_cos = (use_normal && vd.no) ?
+ dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
+ if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
+ (angle_cos > 0.0f)) &&
+ ((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
+ view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength)))
+ {
+ const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
- /* If the vertex is selected for painting. */
- if (!use_vert_sel || mv->flag & SELECT) {
/* Get the average poly color */
uint color_final = 0;
int total_hit_loops = 0;
@@ -2472,8 +2671,8 @@ static void do_vpaint_brush_blur_task_cb_ex(
}
color_orig = ss->mode.vpaint.previous_color[l_index];
}
- const float final_alpha =
- 255 * brush_fade * brush_strength * view_dot *
+ float final_alpha =
+ 255 * brush_fade * brush_strength *
brush_alpha_pressure * grid_alpha;
/* Mix the new color with the original
* based on the brush strength and the curve. */
@@ -2499,13 +2698,13 @@ static void do_vpaint_brush_smear_task_cb_ex(
Scene *scene = CTX_data_scene(data->C);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
- Brush *brush = data->brush;
- StrokeCache *cache = ss->cache;
- const float brush_strength = cache->bstrength;
+ const Brush *brush = data->brush;
+ const StrokeCache *cache = ss->cache;
uint *lcol = data->lcol;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
float brush_dir[3];
+ const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
@@ -2515,14 +2714,15 @@ static void do_vpaint_brush_smear_task_cb_ex(
if (normalize_v3(brush_dir) != 0.0f) {
SculptBrushTest test;
- sculpt_brush_test_init(ss, &test);
+ SculptBrushTestFn sculpt_brush_test_sq_fn =
+ sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
@@ -2533,9 +2733,15 @@ static void do_vpaint_brush_smear_task_cb_ex(
if (!use_vert_sel || mv_curr->flag & SELECT) {
/* Calc the dot prod. between ray norm on surf and current vert
* (ie splash prevention factor), and only paint front facing verts. */
- const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
- if (view_dot > 0.0f) {
- const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
+ float brush_strength = cache->bstrength;
+ const float angle_cos = (use_normal && vd.no) ?
+ dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
+ if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
+ (angle_cos > 0.0f)) &&
+ ((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
+ view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength)))
+ {
+ const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
bool do_color = false;
/* Minimum dot product between brush direction and current
@@ -2592,9 +2798,9 @@ static void do_vpaint_brush_smear_task_cb_ex(
}
color_orig = ss->mode.vpaint.previous_color[l_index];
}
- const float final_alpha =
+ float final_alpha =
255 * brush_fade * brush_strength *
- view_dot * brush_alpha_pressure * grid_alpha;
+ brush_alpha_pressure * grid_alpha;
/* Mix the new color with the original
* based on the brush strength and the curve. */
lcol[l_index] = vpaint_blend(
@@ -2676,27 +2882,18 @@ static void vpaint_paint_leaves(
}
static void vpaint_do_paint(
- bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd,
+ bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd,
Object *ob, Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle)
{
SculptSession *ss = ob->sculpt;
ss->cache->radial_symmetry_pass = i;
sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle);
- SculptSearchSphereData data;
- PBVHNode **nodes = NULL;
- int 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 = true;
- BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
-
- sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, ss->cache->sculpt_normal_symm);
+ int totnode;
+ PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode);
/* Paint those leaves. */
- vpaint_paint_leaves(C, sd, vd, vpd, ob, me, nodes, totnode);
+ vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode);
if (nodes) {
MEM_freeN(nodes);
@@ -2704,31 +2901,31 @@ static void vpaint_do_paint(
}
static void vpaint_do_radial_symmetry(
- bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob, Mesh *me,
+ bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob, Mesh *me,
Brush *brush, const char symm, const int axis)
{
- for (int i = 1; i < vd->radial_symm[axis - 'X']; i++) {
- const float angle = (2.0 * M_PI) * i / vd->radial_symm[axis - 'X'];
- vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, symm, axis, i, angle);
+ for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) {
+ const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X'];
+ vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle);
}
}
/* near duplicate of: sculpt.c's, 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */
static void vpaint_do_symmetrical_brush_actions(
- bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob)
+ bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob)
{
- Brush *brush = BKE_paint_brush(&vd->paint);
+ Brush *brush = BKE_paint_brush(&vp->paint);
Mesh *me = ob->data;
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
- const char symm = vd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ const char symm = vp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
int i = 0;
/* initial stroke */
- vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0);
- vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X');
- vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y');
- vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z');
+ vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
+ vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X');
+ vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y');
+ vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z');
cache->symmetry = symm;
@@ -2740,16 +2937,16 @@ static void vpaint_do_symmetrical_brush_actions(
sculpt_cache_calc_brushdata_symm(cache, i, 0, 0);
if (i & (1 << 0)) {
- vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0);
- vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X');
+ vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
+ vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X');
}
if (i & (1 << 1)) {
- vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Y', 0, 0);
- vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y');
+ vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0);
+ vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y');
}
if (i & (1 << 2)) {
- vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Z', 0, 0);
- vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z');
+ vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0);
+ vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z');
}
}
}
@@ -2824,10 +3021,11 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
- op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, vpaint_stroke_test_start,
- vpaint_stroke_update_step, NULL,
- vpaint_stroke_done, event->type);
-
+ op->customdata = paint_stroke_new(
+ C, op, sculpt_stroke_get_location, vpaint_stroke_test_start,
+ vpaint_stroke_update_step, NULL,
+ vpaint_stroke_done, event->type);
+
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
paint_stroke_data_free(op);
return OPERATOR_FINISHED;
@@ -2838,15 +3036,16 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
OPERATOR_RETVAL_CHECK(retval);
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
-
+
return OPERATOR_RUNNING_MODAL;
}
static int vpaint_exec(bContext *C, wmOperator *op)
{
- op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, vpaint_stroke_test_start,
- vpaint_stroke_update_step, NULL,
- vpaint_stroke_done, 0);
+ op->customdata = paint_stroke_new(
+ C, op, sculpt_stroke_get_location, vpaint_stroke_test_start,
+ vpaint_stroke_update_step, NULL,
+ vpaint_stroke_done, 0);
/* frees op->customdata */
paint_stroke_exec(C, op);
@@ -2871,14 +3070,14 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot)
ot->name = "Vertex Paint";
ot->idname = "PAINT_OT_vertex_paint";
ot->description = "Paint a stroke in the active vertex color layer";
-
+
/* api callbacks */
ot->invoke = vpaint_invoke;
ot->modal = paint_stroke_modal;
ot->exec = vpaint_exec;
ot->poll = vertex_paint_poll;
ot->cancel = vpaint_cancel;
-
+
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c
index a3da5a76dec..3b3e7297aec 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c
@@ -281,7 +281,7 @@ BLI_INLINE uint mcol_darken(uint col1, uint col2, int fac)
BLI_INLINE uint mcol_colordodge(uint col1, uint col2, int fac)
{
uchar *cp1, *cp2, *cp;
- int mfac,temp;
+ int mfac, temp;
uint col = 0;
if (fac == 0) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index 30d62d5a64e..ef86f3c773d 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -676,15 +676,14 @@ static void gradientVertInit__mapFunc(
static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
- int ret = WM_gesture_straightline_modal(C, op, event);
wmGesture *gesture = op->customdata;
DMGradient_vertStoreBase *vert_cache = gesture->userdata;
- bool do_gesture_free = false;
+ int ret = WM_gesture_straightline_modal(C, op, event);
if (ret & OPERATOR_RUNNING_MODAL) {
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { /* XXX, hardcoded */
/* generally crap! redo! */
- do_gesture_free = true;
+ WM_gesture_straightline_cancel(C, op);
ret &= ~OPERATOR_RUNNING_MODAL;
ret |= OPERATOR_FINISHED;
}
@@ -698,16 +697,14 @@ static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEven
BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
wpaint_prev_destroy(&vert_cache->wpp);
}
+ MEM_freeN(vert_cache);
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
}
else if (ret & OPERATOR_FINISHED) {
wpaint_prev_destroy(&vert_cache->wpp);
- }
-
- if (do_gesture_free) {
- WM_gesture_straightline_cancel(C, op);
+ MEM_freeN(vert_cache);
}
return ret;
@@ -743,6 +740,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
sizeof(DMGradient_vertStoreBase) +
(sizeof(DMGradient_vertStore) * me->totvert),
__func__);
+ gesture->userdata_free = false;
data.is_init = true;
wpaint_prev_create(&((DMGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert);
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
index c05b0ba89cf..4d70d82d5c6 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
@@ -185,6 +185,93 @@ BLI_INLINE float wval_darken(const float weight, const float paintval, const flo
return (weight > paintval) ? wval_blend(weight, paintval, alpha) : weight;
}
+/* mainly for color */
+BLI_INLINE float wval_colordodge(float weight, float paintval, float fac)
+{
+ float mfac, temp;
+ if (fac == 0.0f) {
+ return weight;
+ }
+ mfac = 1.0f - fac;
+ temp = (paintval == 1.0f) ? 1.0f : min_ff((weight * (225.0f / 255.0f)) / (1.0f - paintval), 1.0f);
+ return mfac * weight + temp * fac;
+}
+BLI_INLINE float wval_difference(float weight, float paintval, float fac)
+{
+ float mfac, temp;
+ if (fac == 0.0f) {
+ return weight;
+ }
+ mfac = 1.0f - fac;
+ temp = fabsf(weight - paintval);
+ return mfac * weight + temp * fac;
+}
+BLI_INLINE float wval_screen(float weight, float paintval, float fac)
+{
+ float mfac, temp;
+ if (fac == 0.0f) {
+ return weight;
+ }
+ mfac = 1.0f - fac;
+ temp = max_ff(1.0f - (((1.0f - weight) * (1.0f - paintval))), 0);
+ return mfac * weight + temp * fac;
+}
+BLI_INLINE float wval_hardlight(float weight, float paintval, float fac)
+{
+ float mfac, temp;
+ if (fac == 0.0f) {
+ return weight;
+ }
+ mfac = 1.0f - fac;
+ if (paintval > 0.5f) {
+ temp = 1.0f - ((1.0f - 2.0f * (paintval - 0.5f)) * (1.0f - weight));
+ }
+ else {
+ temp = (2.0f * paintval * weight);
+ }
+ return mfac * weight + temp * fac;
+}
+BLI_INLINE float wval_overlay(float weight, float paintval, float fac)
+{
+ float mfac, temp;
+ if (fac == 0.0f) {
+ return weight;
+ }
+ mfac = 1.0f - fac;
+ if (weight > 0.5f) {
+ temp = 1.0f - ((1.0f - 2.0f * (weight - 0.5f)) * (1.0f - paintval));
+ }
+ else {
+ temp = (2.0f * paintval * weight);
+ }
+ return mfac * weight + temp * fac;
+}
+BLI_INLINE float wval_softlight(float weight, float paintval, float fac)
+{
+ float mfac, temp;
+ if (fac == 0.0f) {
+ return weight;
+ }
+ mfac = 1.0f - fac;
+ if (weight < 0.5f) {
+ temp = ((2.0f * ((paintval / 2.0f) + 0.25f)) * weight);
+ }
+ else {
+ temp = 1.0f - (2.0f * (1.0f - ((paintval / 2.0f) + 0.25f)) * (1.0f - weight));
+ }
+ return temp * fac + weight * mfac;
+}
+BLI_INLINE float wval_exclusion(float weight, float paintval, float fac)
+{
+ float mfac, temp;
+ if (fac == 0.0f) {
+ return weight;
+ }
+ mfac = 1.0f - fac;
+ temp = 0.5f - ((2.0f * (weight - 0.5f) * (paintval - 0.5f)));
+ return temp * fac + weight * mfac;
+}
+
/* vpaint has 'vpaint_blend_tool' */
/* result is not clamped from [0-1] */
float ED_wpaint_blend_tool(
@@ -197,15 +284,27 @@ float ED_wpaint_blend_tool(
case PAINT_BLEND_MIX:
case PAINT_BLEND_AVERAGE:
case PAINT_BLEND_SMEAR:
- case PAINT_BLEND_BLUR: return wval_blend(weight, paintval, alpha);
- case PAINT_BLEND_ADD: return wval_add(weight, paintval, alpha);
- case PAINT_BLEND_SUB: return wval_sub(weight, paintval, alpha);
- case PAINT_BLEND_MUL: return wval_mul(weight, paintval, alpha);
- case PAINT_BLEND_LIGHTEN: return wval_lighten(weight, paintval, alpha);
- case PAINT_BLEND_DARKEN: return wval_darken(weight, paintval, alpha);
- default:
- BLI_assert(0);
- return 0.0f;
+ case PAINT_BLEND_BLUR: return wval_blend(weight, paintval, alpha);
+ case PAINT_BLEND_ADD: return wval_add(weight, paintval, alpha);
+ case PAINT_BLEND_SUB: return wval_sub(weight, paintval, alpha);
+ case PAINT_BLEND_MUL: return wval_mul(weight, paintval, alpha);
+ case PAINT_BLEND_LIGHTEN: return wval_lighten(weight, paintval, alpha);
+ case PAINT_BLEND_DARKEN: return wval_darken(weight, paintval, alpha);
+ /* Mostly make sense for color: support anyway. */
+ case PAINT_BLEND_COLORDODGE: return wval_colordodge(weight, paintval, alpha);
+ case PAINT_BLEND_DIFFERENCE: return wval_difference(weight, paintval, alpha);
+ case PAINT_BLEND_SCREEN: return wval_screen(weight, paintval, alpha);
+ case PAINT_BLEND_HARDLIGHT: return wval_hardlight(weight, paintval, alpha);
+ case PAINT_BLEND_OVERLAY: return wval_overlay(weight, paintval, alpha);
+ case PAINT_BLEND_SOFTLIGHT: return wval_softlight(weight, paintval, alpha);
+ case PAINT_BLEND_EXCLUSION: return wval_exclusion(weight, paintval, alpha);
+ /* Only for color: just use blend. */
+ case PAINT_BLEND_LUMINOCITY:
+ case PAINT_BLEND_SATURATION:
+ case PAINT_BLEND_HUE:
+ case PAINT_BLEND_ALPHA_SUB:
+ case PAINT_BLEND_ALPHA_ADD:
+ default: return wval_blend(weight, paintval, alpha);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index d32db8973a0..ac5138deec7 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -523,6 +523,9 @@ void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
copy_v3_v3(test->location, ss->cache->location);
test->dist = 0.0f; /* just for initialize */
+ /* Only for 2D projection. */
+ zero_v4(test->plane);
+
test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass;
if (rv3d->rflag & RV3D_CLIPPING) {
@@ -544,7 +547,7 @@ BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const fl
return ED_view3d_clipping_test(rv3d, symm_co, true);
}
-bool sculpt_brush_test(SculptBrushTest *test, const float co[3])
+bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3])
{
float distsq = len_squared_v3v3(co, test->location);
@@ -560,7 +563,7 @@ bool sculpt_brush_test(SculptBrushTest *test, const float co[3])
}
}
-bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3])
+bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3])
{
float distsq = len_squared_v3v3(co, test->location);
@@ -576,7 +579,7 @@ bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3])
}
}
-bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3])
+bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3])
{
if (sculpt_brush_test_clipping(test, co)) {
return 0;
@@ -584,6 +587,24 @@ bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3])
return len_squared_v3v3(co, test->location) <= test->radius_squared;
}
+bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3])
+{
+ float co_proj[3];
+ closest_to_plane_normalized_v3(co_proj, test->plane, co);
+ float distsq = len_squared_v3v3(co_proj, test->location);
+
+ if (distsq <= test->radius_squared) {
+ if (sculpt_brush_test_clipping(test, co)) {
+ return 0;
+ }
+ test->dist = distsq;
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4])
{
float side = M_SQRT1_2;
@@ -639,7 +660,7 @@ static float frontface(Brush *br, const float sculpt_normal[3],
static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], const float area_no[3])
{
- if (sculpt_brush_test_fast(test, co)) {
+ if (sculpt_brush_test_sphere_fast(test, co)) {
float t1[3], t2[3], t3[3], dist;
sub_v3_v3v3(t1, location, co);
@@ -786,7 +807,7 @@ static void calc_area_normal_and_center_task_cb(void *userdata, const int n)
closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri));
- if (sculpt_brush_test_fast(&test, co)) {
+ if (sculpt_brush_test_sphere_fast(&test, co)) {
float no[3];
int flip_index;
@@ -820,7 +841,7 @@ static void calc_area_normal_and_center_task_cb(void *userdata, const int n)
co = vd.co;
}
- if (sculpt_brush_test_fast(&test, co)) {
+ if (sculpt_brush_test_sphere_fast(&test, co)) {
float no_buf[3];
const float *no;
int flip_index;
@@ -1213,6 +1234,24 @@ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
return len_squared_v3(t) < data->radius_squared;
}
+/* 2D projection (distance to line). */
+bool sculpt_search_circle_cb(PBVHNode *node, void *data_v)
+{
+ SculptSearchCircleData *data = data_v;
+ float bb_min[3], bb_max[3];
+
+ if (data->original)
+ BKE_pbvh_node_get_original_BB(node, bb_min, bb_max);
+ else
+ BKE_pbvh_node_get_BB(node, bb_min, bb_min);
+
+ float dummy_co[3], dummy_depth;
+ const float dist_sq = dist_squared_ray_to_aabb(
+ data->dist_ray_to_aabb_precalc, bb_min, bb_max, dummy_co, &dummy_depth);
+
+ return dist_sq < data->radius_squared;
+}
+
/* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3])
{
@@ -1530,7 +1569,7 @@ static void do_smooth_brush_mesh_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
const float fade = bstrength * tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
@@ -1578,7 +1617,7 @@ static void do_smooth_brush_bmesh_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
const float fade = bstrength * tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, smooth_mask ? 0.0f : *vd.mask,
thread_id);
@@ -1717,7 +1756,7 @@ static void do_smooth_brush_multires_task_cb_ex(
fno = CCG_elem_offset_no(&key, gddata, index);
mask = CCG_elem_offset_mask(&key, gddata, index);
- if (sculpt_brush_test(&test, co)) {
+ if (sculpt_brush_test_sphere(&test, co)) {
const float strength_mask = (smooth_mask ? 0.0f : *mask);
const float fade = bstrength * tex_strength(
ss, brush, co, test.dist, NULL, fno, strength_mask, thread_id);
@@ -1837,7 +1876,7 @@ static void do_mask_brush_draw_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
const float fade = tex_strength(ss, brush, vd.co, test.dist, vd.no, vd.fno, 0.0f, thread_id);
(*vd.mask) += fade * bstrength;
@@ -1897,7 +1936,7 @@ static void do_draw_brush_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
/* offset vertex */
const float fade = tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
@@ -1958,7 +1997,7 @@ static void do_crease_brush_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
/* offset vertex */
const float fade = tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
@@ -2044,7 +2083,7 @@ static void do_pinch_brush_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
const float fade = bstrength * tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
float val[3];
@@ -2096,7 +2135,7 @@ static void do_grab_brush_task_cb_ex(
{
sculpt_orig_vert_data_update(&orig_data, &vd);
- if (sculpt_brush_test(&test, orig_data.co)) {
+ if (sculpt_brush_test_sphere(&test, orig_data.co)) {
const float fade = bstrength * tex_strength(
ss, brush, orig_data.co, test.dist, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f,
thread_id);
@@ -2151,7 +2190,7 @@ static void do_nudge_brush_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
const float fade = bstrength * tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
@@ -2210,7 +2249,7 @@ static void do_snake_hook_brush_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
const float fade = bstrength * tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
@@ -2311,7 +2350,7 @@ static void do_thumb_brush_task_cb_ex(
{
sculpt_orig_vert_data_update(&orig_data, &vd);
- if (sculpt_brush_test(&test, orig_data.co)) {
+ if (sculpt_brush_test_sphere(&test, orig_data.co)) {
const float fade = bstrength * tex_strength(
ss, brush, orig_data.co, test.dist, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f,
thread_id);
@@ -2371,7 +2410,7 @@ static void do_rotate_brush_task_cb_ex(
{
sculpt_orig_vert_data_update(&orig_data, &vd);
- if (sculpt_brush_test(&test, orig_data.co)) {
+ if (sculpt_brush_test_sphere(&test, orig_data.co)) {
float vec[3], rot[3][3];
const float fade = bstrength * tex_strength(
ss, brush, orig_data.co, test.dist, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f,
@@ -2439,7 +2478,7 @@ static void do_layer_brush_task_cb_ex(
{
sculpt_orig_vert_data_update(&orig_data, &vd);
- if (sculpt_brush_test(&test, orig_data.co)) {
+ if (sculpt_brush_test_sphere(&test, orig_data.co)) {
const float fade = bstrength * tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
float *disp = &layer_disp[vd.i];
@@ -2511,7 +2550,7 @@ static void do_inflate_brush_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test(&test, vd.co)) {
+ if (sculpt_brush_test_sphere(&test, vd.co)) {
const float fade = bstrength * tex_strength(
ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
float val[3];
@@ -2617,16 +2656,6 @@ static void calc_sculpt_plane(
}
}
-/* Projects a point onto a plane along the plane's normal */
-static void point_plane_project(
- float intr[3],
- const float co[3], const float plane_normal[3], const float plane_center[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(const StrokeCache *cache, const Brush *brush, const float val[3])
{
return (!(brush->flag & BRUSH_PLANE_TRIM) ||
@@ -2634,26 +2663,18 @@ static int plane_trim(const StrokeCache *cache, const Brush *brush, const float
}
static bool plane_point_side_flip(
- const float co[3], const float plane_normal[3], const float plane_center[3],
+ const float co[3], const float plane[4],
const bool flip)
{
- float delta[3];
- float d;
-
- sub_v3_v3v3(delta, co, plane_center);
- d = dot_v3v3(plane_normal, delta);
-
+ float d = plane_point_side_v3(plane, co);
if (flip) d = -d;
-
return d <= 0.0f;
}
-static int plane_point_side(const float co[3], const float plane_normal[3], const float plane_center[3])
+static int plane_point_side(const float co[3], const float plane[4])
{
- float delta[3];
-
- sub_v3_v3v3(delta, co, plane_center);
- return dot_v3v3(plane_normal, delta) <= 0.0f;
+ float d = plane_point_side_v3(plane, co);
+ return d <= 0.0f;
}
static float get_offset(Sculpt *sd, SculptSession *ss)
@@ -2686,14 +2707,15 @@ static void do_flatten_brush_task_cb_ex(
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
sculpt_brush_test_init(ss, &test);
+ plane_from_point_normal_v3(test.plane, area_co, area_no);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
float intr[3];
float val[3];
- point_plane_project(intr, vd.co, area_no, area_co);
+ closest_to_plane_normalized_v3(intr, test.plane, vd.co);
sub_v3_v3v3(val, intr, vd.co);
@@ -2762,15 +2784,16 @@ static void do_clay_brush_task_cb_ex(
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
sculpt_brush_test_init(ss, &test);
+ plane_from_point_normal_v3(test.plane, area_co, area_no);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test_sq(&test, vd.co)) {
- if (plane_point_side_flip(vd.co, area_no, area_co, flip)) {
+ if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
+ if (plane_point_side_flip(vd.co, test.plane, flip)) {
float intr[3];
float val[3];
- point_plane_project(intr, vd.co, area_no, area_co);
+ closest_to_plane_normalized_v3(intr, test.plane, vd.co);
sub_v3_v3v3(val, intr, vd.co);
@@ -2846,15 +2869,16 @@ static void do_clay_strips_brush_task_cb_ex(
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
sculpt_brush_test_init(ss, &test);
+ plane_from_point_normal_v3(test.plane, area_co, area_no_sp);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_cube(&test, vd.co, mat)) {
- if (plane_point_side_flip(vd.co, area_no_sp, area_co, flip)) {
+ if (plane_point_side_flip(vd.co, test.plane, flip)) {
float intr[3];
float val[3];
- point_plane_project(intr, vd.co, area_no_sp, area_co);
+ closest_to_plane_normalized_v3(intr, test.plane, vd.co);
sub_v3_v3v3(val, intr, vd.co);
@@ -2953,15 +2977,16 @@ static void do_fill_brush_task_cb_ex(
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
sculpt_brush_test_init(ss, &test);
+ plane_from_point_normal_v3(test.plane, area_co, area_no);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test_sq(&test, vd.co)) {
- if (plane_point_side(vd.co, area_no, area_co)) {
+ if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
+ if (plane_point_side(vd.co, test.plane)) {
float intr[3];
float val[3];
- point_plane_project(intr, vd.co, area_no, area_co);
+ closest_to_plane_normalized_v3(intr, test.plane, vd.co);
sub_v3_v3v3(val, intr, vd.co);
@@ -3031,15 +3056,16 @@ static void do_scrape_brush_task_cb_ex(
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
sculpt_brush_test_init(ss, &test);
+ plane_from_point_normal_v3(test.plane, area_co, area_no);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
- if (sculpt_brush_test_sq(&test, vd.co)) {
- if (!plane_point_side(vd.co, area_no, area_co)) {
+ if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
+ if (!plane_point_side(vd.co, test.plane)) {
float intr[3];
float val[3];
- point_plane_project(intr, vd.co, area_no, area_co);
+ closest_to_plane_normalized_v3(intr, test.plane, vd.co);
sub_v3_v3v3(val, intr, vd.co);
@@ -3109,7 +3135,7 @@ static void do_gravity_task_cb_ex(
sculpt_brush_test_init(ss, &test);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
const float fade = tex_strength(
ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f,
thread_id);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 27d8fbaa340..425cecb0010 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -181,10 +181,15 @@ typedef struct SculptBrushTest {
float dist;
int mirror_symmetry_pass;
+ /* For circle (not sphere) projection. */
+ float plane[4];
+
/* View3d clipping - only set rv3d for clipping */
struct RegionView3D *clip_rv3d;
} SculptBrushTest;
+typedef bool (*SculptBrushTestFn)(SculptBrushTest *test, const float co[3]);
+
typedef struct {
struct Sculpt *sd;
struct SculptSession *ss;
@@ -192,12 +197,22 @@ typedef struct {
bool original;
} SculptSearchSphereData;
+typedef struct {
+ struct Sculpt *sd;
+ struct SculptSession *ss;
+ float radius_squared;
+ bool original;
+ struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc;
+} SculptSearchCircleData;
+
void sculpt_brush_test_init(struct SculptSession *ss, SculptBrushTest *test);
-bool sculpt_brush_test(SculptBrushTest *test, const float co[3]);
-bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3]);
-bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3]);
+bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3]);
+bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]);
+bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3]);
bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]);
+bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]);
bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v);
+bool sculpt_search_circle_cb(PBVHNode *node, void *data_v);
float tex_strength(
struct SculptSession *ss, struct Brush *br,
const float point[3],
diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c
index 8e5cf80e013..98aa7c5353b 100644
--- a/source/blender/imbuf/intern/tiff.c
+++ b/source/blender/imbuf/intern/tiff.c
@@ -813,26 +813,51 @@ int imb_savetiff(ImBuf *ibuf, const char *name, int flags)
}
/* copy pixel data. While copying, we flip the image vertically. */
+ const int channels_in_float = ibuf->channels ? ibuf->channels : 4;
for (x = 0; x < ibuf->x; x++) {
for (y = 0; y < ibuf->y; y++) {
- from_i = 4 * (y * ibuf->x + x);
+ from_i = ((size_t)channels_in_float) * (y * ibuf->x + x);
to_i = samplesperpixel * ((ibuf->y - y - 1) * ibuf->x + x);
if (pixels16) {
/* convert from float source */
float rgb[4];
-
- if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) {
- /* float buffer was managed already, no need in color space conversion */
- copy_v3_v3(rgb, &fromf[from_i]);
+
+ if (channels_in_float == 3 || channels_in_float == 4) {
+ if (ibuf->float_colorspace ||
+ (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA))
+ {
+ /* Float buffer was managed already, no need in color
+ * space conversion.
+ */
+ copy_v3_v3(rgb, &fromf[from_i]);
+ }
+ else {
+ /* Standard linear-to-srgb conversion if float buffer
+ * wasn't managed.
+ */
+ linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]);
+ }
+ if (channels_in_float == 4) {
+ rgb[3] = fromf[from_i + 3];
+ }
+ else {
+ rgb[3] = 1.0f;
+ }
}
else {
- /* standard linear-to-srgb conversion if float buffer wasn't managed */
- linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]);
+ if (ibuf->float_colorspace ||
+ (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA))
+ {
+ rgb[0] = fromf[from_i];
+ }
+ else {
+ rgb[0] = linearrgb_to_srgb(fromf[from_i]);
+ }
+ rgb[1] = rgb[2] = rgb[0];
+ rgb[3] = 1.0f;
}
- rgb[3] = fromf[from_i + 3];
-
for (i = 0; i < samplesperpixel; i++, to_i++)
to16[to_i] = FTOUSHORT(rgb[i]);
}
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index e073c6a9f13..4c1accdcdf1 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1121,18 +1121,26 @@ typedef struct UvSculpt {
/* Vertex Paint */
typedef struct VPaint {
Paint paint;
- short flag, pad;
+ short flag;
+ char falloff_shape, normal_angle;
int radial_symm[3]; /* For mirrored painting */
} VPaint;
/* VPaint.flag */
enum {
- // VP_COLINDEX = (1 << 0), /* only paint onto active material*/ /* deprecated since before 2.49 */
- // VP_AREA = (1 << 1), /* deprecated since 2.70 */
- VP_NORMALS = (1 << 3),
- VP_SPRAY = (1 << 4),
- // VP_MIRROR_X = (1 << 5), /* deprecated in 2.5x use (me->editflag & ME_EDIT_MIRROR_X) */
- VP_ONLYVGROUP = (1 << 7) /* weight paint only */
+ VP_FLAG_PROJECT_BACKFACE = (1 << 0),
+ /* TODO */
+ // VP_FLAG_PROJECT_XRAY = (1 << 1),
+ VP_FLAG_PROJECT_FLAT = (1 << 3),
+ VP_FLAG_SPRAY = (1 << 4),
+ /* weight paint only */
+ VP_FLAG_VGROUP_RESTRICT = (1 << 7)
+};
+
+/* VPaint.falloff_shape */
+enum {
+ VP_FALLOFF_SHAPE_SPHERE = 0,
+ VP_FALLOFF_SHAPE_TUBE = 1,
};
/* ------------------------------------------- */
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 106098d22d6..adf647d2dd6 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -690,23 +690,44 @@ static void rna_def_vertex_paint(BlenderRNA *brna)
RNA_def_struct_path_func(srna, "rna_VertexPaint_path");
RNA_def_struct_ui_text(srna, "Vertex Paint", "Properties of vertex and weight paint mode");
- /* vertex paint only */
- prop = RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_NORMALS);
- RNA_def_property_ui_text(prop, "Normals", "Apply the vertex normal before painting");
+ static EnumPropertyItem prop_falloff_items[] = {
+ {VP_FALLOFF_SHAPE_SPHERE, "SPHERE", 0, "Sphere", "Spherical falloff from the brush"},
+ {VP_FALLOFF_SHAPE_TUBE, "TUBE", 0, "Circle", "Circular falloff from the brush along the view"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ prop = RNA_def_property(srna, "falloff_shape", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "falloff_shape");
+ RNA_def_property_enum_items(prop, prop_falloff_items);
+ RNA_def_property_ui_text(prop, "Falloff", "");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+ prop = RNA_def_property(srna, "use_backface_culling", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", VP_FLAG_PROJECT_BACKFACE);
+ RNA_def_property_ui_text(prop, "Cull", "Ignore vertices pointing away from the view (faster)");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+ prop = RNA_def_property(srna, "use_normal_falloff", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", VP_FLAG_PROJECT_FLAT);
+ RNA_def_property_ui_text(prop, "Normals", "Paint most on faces pointing towards the view");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
prop = RNA_def_property(srna, "use_spray", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_SPRAY);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_FLAG_SPRAY);
RNA_def_property_ui_text(prop, "Spray", "Keep applying paint effect while holding mouse");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
/* weight paint only */
prop = RNA_def_property(srna, "use_group_restrict", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_ONLYVGROUP);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_FLAG_VGROUP_RESTRICT);
RNA_def_property_ui_text(prop, "Restrict", "Restrict painting to vertices in the group");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ prop = RNA_def_property(srna, "normal_angle", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_range(prop, 0, 90);
+ RNA_def_property_ui_text(prop, "Angle", "Paint most on faces pointing towards the view according to this angle");
+
/* Mirroring */
prop = RNA_def_property(srna, "radial_symmetry", PROP_INT, PROP_XYZ);
RNA_def_property_int_sdna(prop, NULL, "radial_symm");
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 7fe9f3a8ab4..9df9afcb064 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -428,6 +428,7 @@ typedef struct wmGesture {
/* free pointer to use for operator allocs (if set, its freed on exit)*/
void *userdata;
+ bool userdata_free;
} wmGesture;
/* ************** wmEvent ************************ */
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 30d1fa9d5ca..77030fac5ea 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -71,6 +71,7 @@ wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
gesture->type = type;
gesture->event_type = event->type;
gesture->swinid = ar->swinid; /* means only in area-region context! */
+ gesture->userdata_free = true; /* Free if userdata is set. */
wm_subwindow_origin_get(window, gesture->swinid, &sx, &sy);
@@ -114,7 +115,7 @@ void WM_gesture_end(bContext *C, wmGesture *gesture)
win->tweak = NULL;
BLI_remlink(&win->gesture, gesture);
MEM_freeN(gesture->customdata);
- if (gesture->userdata) {
+ if (gesture->userdata && gesture->userdata_free) {
MEM_freeN(gesture->userdata);
}
MEM_freeN(gesture);