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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py5
-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
13 files changed, 514 insertions, 56 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index a3c8f759f06..89d771b7026 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -364,6 +364,11 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
if not self.is_popover:
brush_basic_sculpt_settings(col, context, brush)
+ # normal_radius_factor
+ col.separator()
+ row = col.row()
+ row.prop(brush, "normal_radius_factor", slider=True)
+
# topology_rake_factor
if (
capabilities.has_topology_rake and
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);