diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_paint.h | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_pbvh.h | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh.c | 50 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_bmesh.c | 21 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pbvh_intern.h | 5 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_280.c | 5 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_cursor.c | 229 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 214 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 14 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 3 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 11 |
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); |