diff options
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); |