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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPablo Dobarro <pablodp606@gmail.com>2019-08-30 17:27:31 +0300
committerPablo Dobarro <pablodp606@gmail.com>2019-08-30 17:46:20 +0300
commite0c792135adf8de3e6a54ddcbab53cab95b2b019 (patch)
tree4a3f7c946a1d11c43baec3ebd60cead5b4b0401d /source/blender
parent8a07ec582e69ba00fc60306eaff2717b68ae3ba7 (diff)
Sculpt: New brush cursor, active vertex and normal radius
This commit includes the new brush cursor, active vertex updates and the normal radius brush property for all sculpt brushes. -The new brush cursor previews the real stroke radius over the mesh and the sampled sculpt normal. -The active vertex is used in sculpt tools and brushes as a starting point for an operation, similar to a preselection. It is also mirrored following the enabled symmetry options to preview the stroke symmetry. -The normal radius brush property limits the radius that is going to be used to sample the sculpt normal and area center. It controls how closely the cursor follows the surface and it improves the behavior of most brushes, making them suitable for hard surface sculpting. Reviewed By: campbellbarton, brecht Differential Revision: https://developer.blender.org/D3594
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_paint.h7
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h5
-rw-r--r--source/blender/blenkernel/intern/brush.c1
-rw-r--r--source/blender/blenkernel/intern/pbvh.c50
-rw-r--r--source/blender/blenkernel/intern/pbvh_bmesh.c21
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h5
-rw-r--r--source/blender/blenloader/intern/versioning_280.c5
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c229
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c214
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h14
-rw-r--r--source/blender/makesdna/DNA_brush_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_brush.c11
12 files changed, 509 insertions, 56 deletions
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 8db27bd4118..ed02a34196f 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -257,8 +257,15 @@ typedef struct SculptSession {
struct StrokeCache *cache;
+ /* Cursor data and active vertex for tools */
int active_vertex_index;
+ float cursor_radius;
+ float cursor_location[3];
+ float cursor_normal[3];
+ float cursor_view_normal[3];
+ struct RegionView3D *rv3d;
+
union {
struct {
struct SculptVertexPaintGeomMap gmap;
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 93a826f3324..f02d41d3c65 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -132,8 +132,11 @@ bool BKE_pbvh_node_raycast(PBVH *bvh,
float (*origco)[3],
bool use_origco,
const float ray_start[3],
+ const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
- float *depth);
+ float *depth,
+ int *active_vertex_index,
+ float *face_normal);
bool BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node,
const float ray_start[3],
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index b2d3ccfebc3..83e4a582ff1 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -72,6 +72,7 @@ static void brush_defaults(Brush *brush)
brush->autosmooth_factor = 0.0f;
brush->topology_rake_factor = 0.0f;
brush->crease_pinch_factor = 0.5f;
+ brush->normal_radius_factor = 0.5f;
brush->sculpt_plane = SCULPT_DISP_DIR_AREA;
/* How far above or below the plane that is found by averaging the faces. */
brush->plane_offset = 0.0f;
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index cea68a0c525..7a8c082842e 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -1753,14 +1753,21 @@ static bool pbvh_faces_node_raycast(PBVH *bvh,
const PBVHNode *node,
float (*origco)[3],
const float ray_start[3],
+ const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
- float *depth)
+ float *depth,
+ int *r_active_vertex_index,
+ float *r_face_normal)
{
const MVert *vert = bvh->verts;
const MLoop *mloop = bvh->mloop;
const int *faces = node->prim_indices;
int i, totface = node->totprim;
bool hit = false;
+ float min_depth = FLT_MAX;
+ float location[3] = {0.0f};
+ float nearest_vertex_co[3];
+ copy_v3_fl(nearest_vertex_co, 0.0f);
for (i = 0; i < totface; ++i) {
const MLoopTri *lt = &bvh->looptri[faces[i]];
@@ -1787,6 +1794,22 @@ static bool pbvh_faces_node_raycast(PBVH *bvh,
vert[mloop[lt->tri[1]].v].co,
vert[mloop[lt->tri[2]].v].co,
depth);
+
+ if (hit && *depth < min_depth) {
+ min_depth = *depth;
+ normal_tri_v3(r_face_normal,
+ vert[mloop[lt->tri[0]].v].co,
+ vert[mloop[lt->tri[1]].v].co,
+ vert[mloop[lt->tri[2]].v].co);
+ madd_v3_v3v3fl(location, ray_start, ray_normal, *depth);
+ for (int j = 0; j < 3; j++) {
+ if (len_squared_v3v3(location, vert[mloop[lt->tri[j]].v].co) <
+ len_squared_v3v3(location, nearest_vertex_co)) {
+ copy_v3_v3(nearest_vertex_co, vert[mloop[lt->tri[j]].v].co);
+ *r_active_vertex_index = mloop[lt->tri[j]].v;
+ }
+ }
+ }
}
}
@@ -1857,8 +1880,11 @@ bool BKE_pbvh_node_raycast(PBVH *bvh,
float (*origco)[3],
bool use_origco,
const float ray_start[3],
+ const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
- float *depth)
+ float *depth,
+ int *active_vertex_index,
+ float *face_normal)
{
bool hit = false;
@@ -1868,13 +1894,29 @@ bool BKE_pbvh_node_raycast(PBVH *bvh,
switch (bvh->type) {
case PBVH_FACES:
- hit |= pbvh_faces_node_raycast(bvh, node, origco, ray_start, isect_precalc, depth);
+ hit |= pbvh_faces_node_raycast(bvh,
+ node,
+ origco,
+ ray_start,
+ ray_normal,
+ isect_precalc,
+ depth,
+ active_vertex_index,
+ face_normal);
break;
case PBVH_GRIDS:
hit |= pbvh_grids_node_raycast(bvh, node, origco, ray_start, isect_precalc, depth);
break;
case PBVH_BMESH:
- hit = pbvh_bmesh_node_raycast(node, ray_start, isect_precalc, depth, use_origco);
+ BM_mesh_elem_index_ensure(bvh->bm, BM_VERT);
+ hit = pbvh_bmesh_node_raycast(node,
+ ray_start,
+ ray_normal,
+ isect_precalc,
+ depth,
+ use_origco,
+ active_vertex_index,
+ face_normal);
break;
}
diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c
index 1d8088c6605..c04e172f116 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -1508,12 +1508,18 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx,
bool pbvh_bmesh_node_raycast(PBVHNode *node,
const float ray_start[3],
+ const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
float *depth,
- bool use_original)
+ bool use_original,
+ int *r_active_vertex_index,
+ float *r_face_normal)
{
bool hit = false;
+ float min_depth = FLT_MAX;
+ float nearest_vertex_co[3] = {0.0f};
+ float location[3] = {0.0f};
if (use_original && node->bm_tot_ortri) {
for (int i = 0; i < node->bm_tot_ortri; i++) {
const int *t = node->bm_ortri[i];
@@ -1538,6 +1544,19 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node,
BM_face_as_array_vert_tri(f, v_tri);
hit |= ray_face_intersection_tri(
ray_start, isect_precalc, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth);
+
+ if (hit && *depth < min_depth) {
+ min_depth = *depth;
+ normal_tri_v3(r_face_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
+ madd_v3_v3v3fl(location, ray_start, ray_normal, *depth);
+ for (int j = 0; j < 3; j++) {
+ if (len_squared_v3v3(location, v_tri[j]->co) <
+ len_squared_v3v3(location, nearest_vertex_co)) {
+ copy_v3_v3(nearest_vertex_co, v_tri[j]->co);
+ *r_active_vertex_index = BM_elem_index_get(v_tri[j]);
+ }
+ }
+ }
}
}
}
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index e74a8d43c68..bad103743eb 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -206,9 +206,12 @@ void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, int flag);
/* pbvh_bmesh.c */
bool pbvh_bmesh_node_raycast(PBVHNode *node,
const float ray_start[3],
+ const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
float *dist,
- bool use_original);
+ bool use_original,
+ int *r_active_vertex_index,
+ float *r_face_normal);
bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node,
const float ray_start[3],
const float ray_normal[3],
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index a66b9336632..ed3b1613b2a 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -3706,5 +3706,10 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* Versioning code until next subversion bump goes here. */
+ for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
+ if (br->ob_mode & OB_MODE_SCULPT && br->normal_radius_factor == 0.0f) {
+ br->normal_radius_factor = 0.5f;
+ }
+ }
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 65e10f98753..f456adfdf19 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -45,13 +45,17 @@
#include "BKE_node.h"
#include "BKE_paint.h"
#include "BKE_colortools.h"
+#include "BKE_object.h"
#include "WM_api.h"
+#include "wm_cursors.h"
#include "IMB_imbuf_types.h"
#include "ED_view3d.h"
+#include "DEG_depsgraph.h"
+
#include "GPU_draw.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
@@ -602,7 +606,7 @@ static bool sculpt_get_brush_geometry(bContext *C,
/* Draw an overlay that shows what effect the brush's texture will
* have on brush strength */
-static void paint_draw_tex_overlay(UnifiedPaintSettings *ups,
+static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
Brush *brush,
ViewContext *vc,
int x,
@@ -622,7 +626,7 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups,
if (!(mtex->tex) ||
!((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) ||
(valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) {
- return;
+ return false;
}
if (load_tex(brush, vc, zoom, col, primary)) {
@@ -728,18 +732,19 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups,
GPU_matrix_pop();
}
}
+ return true;
}
/* Draw an overlay that shows what effect the brush's texture will
* have on brush strength */
-static void paint_draw_cursor_overlay(
+static bool paint_draw_cursor_overlay(
UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, int y, float zoom)
{
rctf quad;
/* check for overlay mode */
if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) {
- return;
+ return false;
}
if (load_tex_cursor(brush, vc, zoom)) {
@@ -811,9 +816,10 @@ static void paint_draw_cursor_overlay(
GPU_matrix_pop();
}
}
+ return true;
}
-static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
+static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
Brush *brush,
ViewContext *vc,
int x,
@@ -824,6 +830,9 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
/* Color means that primary brush texture is colored and
* secondary is used for alpha/mask control. */
bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX);
+
+ bool alpha_overlay_active = false;
+
eOverlayControlFlags flags = BKE_paint_get_overlay_flags();
gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT);
@@ -836,26 +845,28 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
/* Colored overlay should be drawn separately. */
if (col) {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) {
- paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) {
- paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
- paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
+ alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
}
}
else {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) {
- paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
- paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
+ alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
}
}
GPU_matrix_pop();
gpuPopAttr();
+
+ return alpha_overlay_active;
}
BLI_INLINE void draw_tri_point(
@@ -1074,6 +1085,98 @@ static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush)
return true;
}
+static void cursor_draw_point_screen_space(const uint gpuattr,
+ const ARegion *ar,
+ float true_location[3],
+ float obmat[4][4])
+{
+ float translation_vertex_cursor[2], location[3];
+ copy_v3_v3(location, true_location);
+ mul_m4_v3(obmat, location);
+ ED_view3d_project(ar, location, translation_vertex_cursor);
+ imm_draw_circle_fill_3d(
+ gpuattr, translation_vertex_cursor[0], translation_vertex_cursor[1], 3, 10);
+}
+
+static void cursor_draw_tiling_preview(const uint gpuattr,
+ const ARegion *ar,
+ float true_location[3],
+ Sculpt *sd,
+ Object *ob,
+ float radius)
+{
+ BoundBox *bb = BKE_object_boundbox_get(ob);
+ float orgLoc[3], location[3];
+ int dim, tile_pass = 0;
+ int start[3];
+ int end[3];
+ int cur[3];
+ const float *bbMin = bb->vec[0];
+ const float *bbMax = bb->vec[6];
+ const float *step = sd->paint.tile_offset;
+
+ copy_v3_v3(orgLoc, true_location);
+ for (dim = 0; dim < 3; ++dim) {
+ if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) {
+ start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim];
+ end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim];
+ }
+ else
+ start[dim] = end[dim] = 0;
+ }
+ copy_v3_v3_int(cur, start);
+ for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) {
+ for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) {
+ for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) {
+ if (!cur[0] && !cur[1] && !cur[2])
+ continue; /* skip tile at orgLoc, this was already handled before all others */
+ tile_pass++;
+ for (dim = 0; dim < 3; dim++) {
+ location[dim] = cur[dim] * step[dim] + orgLoc[dim];
+ }
+ cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat);
+ }
+ }
+ }
+}
+
+static void cursor_draw_point_with_symmetry(const uint gpuattr,
+ const ARegion *ar,
+ const float true_location[3],
+ Sculpt *sd,
+ Object *ob,
+ float radius)
+{
+ const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ float location[3], symm_rot_mat[4][4];
+
+ for (int i = 0; i <= symm; ++i) {
+ if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
+
+ /* Axis Symmetry */
+ flip_v3_v3(location, true_location, (char)i);
+ cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat);
+
+ /* Tiling */
+ cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius);
+
+ /* Radial Symmetry */
+ for (char raxis = 0; raxis < 3; raxis++) {
+ for (int r = 1; r < sd->radial_symm[raxis]; r++) {
+ float angle = 2 * M_PI * r / sd->radial_symm[(int)raxis];
+ flip_v3_v3(location, true_location, (char)i);
+ unit_m4(symm_rot_mat);
+ rotate_m4(symm_rot_mat, raxis + 'X', angle);
+ mul_m4_v3(symm_rot_mat, location);
+
+ cursor_draw_tiling_preview(gpuattr, ar, location, sd, ob, radius);
+ cursor_draw_point_screen_space(gpuattr, ar, location, ob->obmat);
+ }
+ }
+ }
+ }
+}
+
static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
{
Scene *scene = CTX_data_scene(C);
@@ -1121,7 +1224,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
}
/* draw overlay */
- paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode);
+ bool alpha_overlay_active = paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode);
/* TODO: as sculpt and other paint modes are unified, this
* special mode of drawing will go away */
@@ -1158,12 +1261,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
}
/* make lines pretty */
- GPU_line_width(1.0f);
+ GPU_line_width(2.0f);
GPU_blend(true); /* TODO: also set blend mode? */
GPU_line_smooth(true);
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* set brush color */
immUniformColor3fvAlpha(outline_col, outline_alpha);
@@ -1176,7 +1279,103 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
/* outer at half alpha */
immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f);
}
- imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40);
+
+ /* Only sculpt mode cursor for now */
+
+ /* Disable for PBVH_GRIDS */
+ SculptSession *ss = vc.obact->sculpt;
+ bool is_multires = ss && ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS;
+
+ if ((mode == PAINT_MODE_SCULPT) && ss && !is_multires &&
+ !(brush->falloff_shape & BRUSH_AIRBRUSH)) {
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ wmWindow *win = CTX_wm_window(C);
+
+ /* Update WM mouse cursor, disable when the 3D brush cursor is enabled */
+ if (sd->paint.brush->overlay_flags & BRUSH_OVERLAY_CURSOR) {
+ WM_cursor_set(win, CURSOR_STD);
+ }
+ else {
+ WM_cursor_set(win, CURSOR_EDIT);
+ }
+
+ if (!ups->stroke_active) {
+ SculptCursorGeometryInfo gi;
+ float mouse[2] = {x - ar->winrct.xmin, y - ar->winrct.ymin};
+ if (sculpt_cursor_geometry_info_update(C, &gi, mouse, true) && !alpha_overlay_active) {
+
+ float rds;
+ if (!BKE_brush_use_locked_size(scene, brush)) {
+ rds = paint_calc_object_space_radius(&vc, gi.location, BKE_brush_size_get(scene, brush));
+ }
+ else {
+ rds = BKE_brush_unprojected_radius_get(scene, brush);
+ }
+
+ wmViewport(&ar->winrct);
+
+ /* Draw 3D active vertex preview with symmetry*/
+ if (len_v3v3(gi.active_vertex_co, gi.location) < rds) {
+ cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds);
+ }
+
+ /* Draw 3D brush cursor */
+ GPU_matrix_push_projection();
+ ED_view3d_draw_setup_view(CTX_wm_window(C),
+ CTX_data_depsgraph_pointer(C),
+ CTX_data_scene(C),
+ ar,
+ CTX_wm_view3d(C),
+ NULL,
+ NULL,
+ NULL);
+
+ float cursor_trans[4][4], cursor_rot[4][4];
+ float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
+ float quat[4];
+
+ copy_m4_m4(cursor_trans, vc.obact->obmat);
+ translate_m4(cursor_trans, gi.location[0], gi.location[1], gi.location[2]);
+ rotation_between_vecs_to_quat(quat, z_axis, gi.normal);
+ quat_to_mat4(cursor_rot, quat);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(cursor_trans);
+ GPU_matrix_mul(cursor_rot);
+ imm_draw_circle_wire_3d(pos, 0, 0, rds, 40);
+ GPU_matrix_pop();
+
+ GPU_matrix_pop_projection();
+
+ wmWindowViewport(win);
+ }
+ else {
+ /* Draw default cursor when the mouse is not over the mesh or there are no supported
+ * overlays active */
+ GPU_line_width(1.0f);
+ imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40);
+ }
+ }
+ else {
+ if (vc.obact->sculpt->cache && !vc.obact->sculpt->cache->first_time) {
+ /* Draw cursor location preview when the stroke is active using the data from StrokeCache
+ */
+ float cursor_location[3];
+ wmViewport(&ar->winrct);
+ copy_v3_v3(cursor_location, ss->cache->true_location);
+ if (ss->cache->brush->sculpt_tool == SCULPT_TOOL_GRAB) {
+ add_v3_v3(cursor_location, ss->cache->grab_delta);
+ }
+ cursor_draw_point_with_symmetry(pos, ar, cursor_location, sd, vc.obact, ss->cache->radius);
+ wmWindowViewport(win);
+ }
+ }
+ }
+ else {
+ /* Draw default cursor in unsupported modes */
+ GPU_line_width(1.0f);
+ imm_draw_circle_wire_3d(pos, translation[0], translation[1], final_radius, 40);
+ }
immUnbindProgram();
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index ea2f38139a4..5aa913ad006 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -96,7 +96,18 @@
/* Do not use these functions while working with PBVH_GRIDS data in SculptSession */
-/* TODO: why is this kept, should it be removed? */
+static float *sculpt_vertex_co_get(SculptSession *ss, int index)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES:
+ return ss->mvert[index].co;
+ case PBVH_BMESH:
+ return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
+ default:
+ return NULL;
+ }
+}
+
#if 0 /* UNUSED */
static int sculpt_active_vertex_get(SculptSession *ss)
@@ -136,18 +147,6 @@ static void sculpt_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
-static float *sculpt_vertex_co_get(SculptSession *ss, int index)
-{
- switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- return ss->mvert[index].co;
- case PBVH_BMESH:
- return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
- default:
- return NULL;
- }
-}
-
static void sculpt_vertex_co_set(SculptSession *ss, int index, float co[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
@@ -748,17 +747,26 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar, Object *ob)
void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
{
- RegionView3D *rv3d = ss->cache->vc->rv3d;
+ RegionView3D *rv3d = ss->cache ? ss->cache->vc->rv3d : ss->rv3d;
+
+ test->radius_squared = ss->cache ? ss->cache->radius_squared :
+ ss->cursor_radius * ss->cursor_radius;
+ if (ss->cache) {
+ copy_v3_v3(test->location, ss->cache->location);
+ test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass;
+ }
+ else {
+ copy_v3_v3(test->location, ss->cursor_location);
+ test->mirror_symmetry_pass = 0;
+ }
- test->radius_squared = ss->cache->radius_squared;
- copy_v3_v3(test->location, ss->cache->location);
test->dist = 0.0f; /* just for initialize */
/* Only for 2D projection. */
zero_v4(test->plane_view);
zero_v4(test->plane_tool);
- test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass;
+ test->mirror_symmetry_pass = ss->cache ? ss->cache->mirror_symmetry_pass : 0;
if (rv3d->rflag & RV3D_CLIPPING) {
test->clip_rv3d = rv3d;
@@ -1050,7 +1058,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
int private_count[2] = {0};
bool use_original = false;
- if (ss->cache->original) {
+ if (ss->cache && ss->cache->original) {
unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
use_original = (unode->co || unode->bm_entry);
}
@@ -1059,6 +1067,13 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
+ /* Update the test radius to sample the normal using the normal radius of the brush */
+ if (data->brush->ob_mode == OB_MODE_SCULPT) {
+ float test_radius = sqrtf(test.radius_squared);
+ test_radius *= data->brush->normal_radius_factor;
+ test.radius_squared = test_radius * test_radius;
+ }
+
/* when the mesh is edited we can't rely on original coords
* (original mesh may not even have verts in brush radius) */
if (use_original && data->has_bm_orco) {
@@ -1120,6 +1135,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
const float *no;
int flip_index;
+ data->any_vertex_sampled = true;
+
if (use_original) {
normal_short_to_float_v3(no_buf, no_s);
no = no_buf;
@@ -1134,7 +1151,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
}
}
- flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f);
+ flip_index = (dot_v3v3(ss->cache ? ss->cache->view_normal : ss->cursor_view_normal, no) <=
+ 0.0f);
if (area_cos) {
add_v3_v3(private_co[flip_index], co);
}
@@ -1223,7 +1241,7 @@ static void calc_area_normal(
}
/* expose 'calc_area_normal' externally. */
-void sculpt_pbvh_calc_area_normal(const Brush *brush,
+bool sculpt_pbvh_calc_area_normal(const Brush *brush,
Object *ob,
PBVHNode **nodes,
int totnode,
@@ -1249,6 +1267,7 @@ void sculpt_pbvh_calc_area_normal(const Brush *brush,
.area_cos = NULL,
.area_nos = area_nos,
.count = count,
+ .any_vertex_sampled = false,
};
BLI_mutex_init(&data.mutex);
@@ -1265,6 +1284,8 @@ void sculpt_pbvh_calc_area_normal(const Brush *brush,
break;
}
}
+
+ return data.any_vertex_sampled;
}
/* this calculates flatten center and area normal together,
@@ -1508,7 +1529,8 @@ float tex_strength(SculptSession *ss,
bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
{
SculptSearchSphereData *data = data_v;
- float *center = data->ss->cache->location, nearest[3];
+ float *center, nearest[3];
+ center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location;
float t[3], bb_min[3], bb_max[3];
int i;
@@ -1585,12 +1607,13 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = NULL;
- /* Build a list of all nodes that are potentially within the brush's area of influence */
+ /* Build a list of all nodes that are potentially within the cursor or brush's area of influence
+ */
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
SculptSearchSphereData data = {
.ss = ss,
.sd = sd,
- .radius_squared = SQUARE(ss->cache->radius * radius_scale),
+ .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius,
.original = use_original,
};
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode);
@@ -1602,7 +1625,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
SculptSearchCircleData data = {
.ss = ss,
.sd = sd,
- .radius_squared = SQUARE(ss->cache->radius * radius_scale),
+ .radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius,
.original = use_original,
.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
};
@@ -1964,10 +1987,14 @@ typedef struct SculptDoBrushSmoothGridDataChunk {
typedef struct {
SculptSession *ss;
const float *ray_start;
+ const float *ray_normal;
bool hit;
float depth;
bool original;
+ int active_vertex_index;
+ float *face_normal;
+
struct IsectRayPrecalc isect_precalc;
} SculptRaycastData;
@@ -4513,7 +4540,7 @@ static void do_tiled(
float orgLoc[3]; /* position of the "prototype" stroke for tiling */
copy_v3_v3(orgLoc, cache->location);
- for (dim = 0; dim < 3; ++dim) {
+ for (dim = 0; dim < 3; dim++) {
if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) {
start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim];
end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim];
@@ -4529,16 +4556,16 @@ static void do_tiled(
/* now do it for all the tiles */
copy_v3_v3_int(cur, start);
- for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) {
- for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) {
- for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) {
+ for (cur[0] = start[0]; cur[0] <= end[0]; cur[0]++) {
+ for (cur[1] = start[1]; cur[1] <= end[1]; cur[1]++) {
+ for (cur[2] = start[2]; cur[2] <= end[2]; cur[2]++) {
if (!cur[0] && !cur[1] && !cur[2]) {
continue; /* skip tile at orgLoc, this was already handled before all others */
}
++cache->tile_pass;
- for (dim = 0; dim < 3; ++dim) {
+ for (dim = 0; dim < 3; dim++) {
cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim];
cache->plane_offset[dim] = cur[dim] * step[dim];
}
@@ -5187,8 +5214,11 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
origco,
use_origco,
srd->ray_start,
+ srd->ray_normal,
&srd->isect_precalc,
- &srd->depth)) {
+ &srd->depth,
+ &srd->active_vertex_index,
+ srd->face_normal)) {
srd->hit = 1;
*tmin = srd->depth;
}
@@ -5276,6 +5306,120 @@ static float sculpt_raycast_init(ViewContext *vc,
return dist;
}
+/* Gets the normal, location and active vertex location of the geometry under the cursor. This also
+ * updates
+ * the active vertex and cursor related data of the SculptSession using the mouse position */
+bool sculpt_cursor_geometry_info_update(bContext *C,
+ SculptCursorGeometryInfo *out,
+ const float mouse[2],
+ bool use_sampled_normal)
+{
+ Scene *scene = CTX_data_scene(C);
+ Sculpt *sd = scene->toolsettings->sculpt;
+ Object *ob;
+ SculptSession *ss;
+ ViewContext vc;
+ const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
+ float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3], sampled_normal[3],
+ mat[3][3];
+ float viewDir[3] = {0.0f, 0.0f, 1.0f};
+ int totnode;
+ bool original = false, hit = false;
+
+ ED_view3d_viewcontext_init(C, &vc);
+
+ ob = vc.obact;
+ ss = ob->sculpt;
+
+ if (!ss->pbvh) {
+ copy_v3_fl(out->location, 0.0f);
+ copy_v3_fl(out->normal, 0.0f);
+ copy_v3_fl(out->active_vertex_co, 0.0f);
+ return false;
+ }
+
+ /* PBVH raycast to get active vertex and face normal */
+ depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original);
+ sculpt_stroke_modifiers_check(C, ob, brush);
+
+ SculptRaycastData srd = {
+ .original = original,
+ .ss = ob->sculpt,
+ .hit = 0,
+ .ray_start = ray_start,
+ .ray_normal = ray_normal,
+ .depth = depth,
+ .face_normal = face_normal,
+ };
+ isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
+ BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original);
+
+ /* Cursor is not over the mesh, return default values */
+ if (!srd.hit) {
+ copy_v3_fl(out->location, 0.0f);
+ copy_v3_fl(out->normal, 0.0f);
+ copy_v3_fl(out->active_vertex_co, 0.0f);
+ return false;
+ }
+
+ /* Update the active vertex of the SculptSession */
+ ss->active_vertex_index = srd.active_vertex_index;
+
+ copy_v3_v3(out->active_vertex_co, sculpt_vertex_co_get(ss, srd.active_vertex_index));
+ copy_v3_v3(out->location, ray_normal);
+ mul_v3_fl(out->location, srd.depth);
+ add_v3_v3(out->location, ray_start);
+
+ /* Option to return the face normal directly for performance o accuracy reasons */
+ if (!use_sampled_normal) {
+ copy_v3_v3(out->normal, srd.face_normal);
+ return hit;
+ }
+
+ /* Sampled normal calculation */
+ const float radius_scale = 1.0f;
+ float radius;
+
+ /* Update cursor data in SculptSession */
+ invert_m4_m4(ob->imat, ob->obmat);
+ copy_m3_m4(mat, vc.rv3d->viewinv);
+ mul_m3_v3(mat, viewDir);
+ copy_m3_m4(mat, ob->imat);
+ mul_m3_v3(mat, viewDir);
+ normalize_v3_v3(ss->cursor_view_normal, viewDir);
+ copy_v3_v3(ss->cursor_normal, srd.face_normal);
+ copy_v3_v3(ss->cursor_location, out->location);
+ ss->rv3d = vc.rv3d;
+
+ if (!BKE_brush_use_locked_size(scene, brush)) {
+ radius = paint_calc_object_space_radius(&vc, out->location, BKE_brush_size_get(scene, brush));
+ }
+ else {
+ radius = BKE_brush_unprojected_radius_get(scene, brush);
+ }
+ ss->cursor_radius = radius;
+
+ PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, original, radius_scale, &totnode);
+
+ /* In case there are no nodes under the cursor, return the face normal */
+ if (!totnode) {
+ MEM_freeN(nodes);
+ copy_v3_v3(out->normal, srd.face_normal);
+ return true;
+ }
+
+ /* Calculate the sampled normal */
+ if (sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, sampled_normal)) {
+ copy_v3_v3(out->normal, sampled_normal);
+ }
+ else {
+ /* Use face normal when there are no vertices to sample inside the cursor radius */
+ copy_v3_v3(out->normal, srd.face_normal);
+ }
+ MEM_freeN(nodes);
+ return true;
+}
+
/* Do a raycast in the tree to find the 3d brush location
* (This allows us to ignore the GL depth buffer)
* Returns 0 if the ray doesn't hit the mesh, non-zero otherwise
@@ -5285,7 +5429,7 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2])
Object *ob;
SculptSession *ss;
StrokeCache *cache;
- float ray_start[3], ray_end[3], ray_normal[3], depth;
+ float ray_start[3], ray_end[3], ray_normal[3], depth, face_normal[3];
bool original;
ViewContext vc;
@@ -5303,14 +5447,21 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2])
depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original);
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BM_mesh_elem_table_ensure(ss->bm, BM_VERT);
+ BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
+ }
+
bool hit = false;
{
SculptRaycastData srd;
srd.ss = ob->sculpt;
srd.ray_start = ray_start;
+ srd.ray_normal = ray_normal;
srd.hit = 0;
srd.depth = depth;
srd.original = original;
+ srd.face_normal = face_normal;
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original);
@@ -5578,7 +5729,6 @@ static void sculpt_stroke_update_step(bContext *C,
}
do_symmetrical_brush_actions(sd, ob, do_brush_action, ups);
-
sculpt_combine_proxies(sd, ob);
/* hack to fix noise texture tearing mesh */
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index bc9a4c1d85b..9f1cb7a53a4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -45,7 +45,18 @@ bool sculpt_poll(struct bContext *C);
bool sculpt_poll_view3d(struct bContext *C);
/* Stroke */
+
+typedef struct SculptCursorGeometryInfo {
+ float location[3];
+ float normal[3];
+ float active_vertex_co[3];
+} SculptCursorGeometryInfo;
+
bool sculpt_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]);
+bool sculpt_cursor_geometry_info_update(bContext *C,
+ SculptCursorGeometryInfo *out,
+ const float mouse[2],
+ bool use_sampled_normal);
/* Dynamic topology */
void sculpt_pbvh_clear(Object *ob);
@@ -162,6 +173,7 @@ typedef struct SculptThreadedTaskData {
float (*area_cos)[3];
float (*area_nos)[3];
int *count;
+ bool any_vertex_sampled;
ThreadMutex mutex;
@@ -226,7 +238,7 @@ float tex_strength(struct SculptSession *ss,
const int thread_id);
/* just for vertex paint. */
-void sculpt_pbvh_calc_area_normal(const struct Brush *brush,
+bool sculpt_pbvh_calc_area_normal(const struct Brush *brush,
Object *ob,
PBVHNode **nodes,
int totnode,
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 78aa68556cb..bae9c8f40ea 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -295,6 +295,8 @@ typedef struct Brush {
float crease_pinch_factor;
+ float normal_radius_factor;
+
float plane_trim;
/** Affectable height of brush (layer height for layer tool, i.e.). */
float height;
@@ -302,7 +304,6 @@ typedef struct Brush {
float texture_sample_bias;
int curve_preset;
- char _pad1[4];
/* overlay */
int texture_overlay_alpha;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index a51897bc340..14aa9c5d8cf 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1845,6 +1845,17 @@ static void rna_def_brush(BlenderRNA *brna)
"Best used on low-poly meshes as it has a performance impact");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "normal_radius_factor", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "normal_radius_factor");
+ RNA_def_property_float_default(prop, 0.5f);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+ RNA_def_property_ui_text(prop,
+ "Normal Radius",
+ "Ratio between the brush radius and the radius that is going to be "
+ "used to sample the normal");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "stencil_pos", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "stencil_pos");
RNA_def_property_array(prop, 2);