Cleanup: Various non-functional changes for sculpt raycast methods

* Removes `SculptSession` from being passed to the raycast callbacks,
  it was only used to determine the existence of the `StrokeCache` and
  has been replaced with a boolean.
* Use C++ math library where possible.
* Use const where possible.
* Moves most related `SCULPT_` prefixed methods into the corresponding
  namespace.
  * Renames `SCULPT_stroke_get_location` to `stroke_get_location_bvh`
    to avoid potential collisions in the `sculpt_paint` namespace with
    other editors & be more clear about the usage.

Pull Request: https://projects.blender.org/blender/blender/pulls/138796
This commit is contained in:
Sean Kim
2025-05-15 05:22:46 +02:00
committed by Sean Kim
parent b84e790b81
commit 15c80a29a8
16 changed files with 299 additions and 308 deletions

View File

@@ -1412,14 +1412,14 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext &pcon
Scene &scene = *pcontext.scene;
UnifiedPaintSettings &ups = *pcontext.ups;
ViewContext &vc = pcontext.vc;
SculptCursorGeometryInfo gi;
CursorGeometryInfo gi;
const float2 mval_fl = {
float(pcontext.mval.x - pcontext.region->winrct.xmin),
float(pcontext.mval.y - pcontext.region->winrct.ymin),
};
/* Ensure that the PBVH is generated before we call #SCULPT_cursor_geometry_info_update because
/* Ensure that the PBVH is generated before we call #cursor_geometry_info_update because
* the PBVH is needed to do a ray-cast to find the active vertex. */
bke::object::pbvh_ensure(*pcontext.depsgraph, *pcontext.vc.obact);
@@ -1427,7 +1427,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext &pcon
* work correctly */
pcontext.prev_active_vert_index = ss.active_vert_index();
if (!ups.stroke_active) {
pcontext.is_cursor_over_mesh = SCULPT_cursor_geometry_info_update(
pcontext.is_cursor_over_mesh = cursor_geometry_info_update(
C, &gi, mval_fl, (pcontext.brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE));
pcontext.location = gi.location;
pcontext.normal = gi.normal;

View File

@@ -592,7 +592,7 @@ static void paint_brush_stroke_add_step(
if (paint_stroke_use_scene_spacing(brush, mode)) {
float3 world_space_position;
if (SCULPT_stroke_get_location(
if (stroke_get_location_bvh(
C, world_space_position, stroke->last_mouse_position, stroke->original))
{
stroke->last_world_space_position = math::transform_point(
@@ -835,7 +835,7 @@ static int paint_space_stroke(bContext *C,
const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
if (use_scene_spacing) {
float3 world_space_position;
const bool hit = SCULPT_stroke_get_location(
const bool hit = stroke_get_location_bvh(
C, world_space_position, final_mouse, stroke->original);
world_space_position = math::transform_point(stroke->vc.obact->object_to_world(),
world_space_position);
@@ -1255,11 +1255,11 @@ static void paint_line_strokes_spacing(bContext *C,
stroke->last_mouse_position = old_pos;
if (use_scene_spacing) {
const bool hit_old = SCULPT_stroke_get_location(
const bool hit_old = stroke_get_location_bvh(
C, world_space_position_old, old_pos, stroke->original);
float3 world_space_position_new;
const bool hit_new = SCULPT_stroke_get_location(
const bool hit_new = stroke_get_location_bvh(
C, world_space_position_new, new_pos, stroke->original);
world_space_position_old = math::transform_point(stroke->vc.obact->object_to_world(),
@@ -1404,7 +1404,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
copy_v2_v2(stroke->last_mouse_position, data + 2 * j);
if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) {
stroke->stroke_over_mesh = SCULPT_stroke_get_location(
stroke->stroke_over_mesh = stroke_get_location_bvh(
C, stroke->last_world_space_position, data + 2 * j, stroke->original);
mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
}
@@ -1532,7 +1532,7 @@ wmOperatorStatus paint_stroke_modal(bContext *C,
stroke->last_pressure = sample_average.pressure;
stroke->last_mouse_position = sample_average.mouse;
if (paint_stroke_use_scene_spacing(*br, mode)) {
stroke->stroke_over_mesh = SCULPT_stroke_get_location(
stroke->stroke_over_mesh = stroke_get_location_bvh(
C, stroke->last_world_space_position, sample_average.mouse, stroke->original);
stroke->last_world_space_position = math::transform_point(
stroke->vc.obact->object_to_world(), stroke->last_world_space_position);

View File

@@ -2029,7 +2029,7 @@ static wmOperatorStatus vpaint_invoke(bContext *C, wmOperator *op, const wmEvent
{
op->customdata = paint_stroke_new(C,
op,
SCULPT_stroke_get_location,
stroke_get_location_bvh,
vpaint_stroke_test_start,
vpaint_stroke_update_step,
nullptr,
@@ -2055,7 +2055,7 @@ static wmOperatorStatus vpaint_exec(bContext *C, wmOperator *op)
{
op->customdata = paint_stroke_new(C,
op,
SCULPT_stroke_get_location,
stroke_get_location_bvh,
vpaint_stroke_test_start,
vpaint_stroke_update_step,
nullptr,

View File

@@ -1905,7 +1905,7 @@ static wmOperatorStatus wpaint_invoke(bContext *C, wmOperator *op, const wmEvent
{
op->customdata = paint_stroke_new(C,
op,
SCULPT_stroke_get_location,
stroke_get_location_bvh,
wpaint_stroke_test_start,
wpaint_stroke_update_step,
nullptr,
@@ -1930,7 +1930,7 @@ static wmOperatorStatus wpaint_exec(bContext *C, wmOperator *op)
{
op->customdata = paint_stroke_new(C,
op,
SCULPT_stroke_get_location,
stroke_get_location_bvh,
wpaint_stroke_test_start,
wpaint_stroke_update_step,
nullptr,

View File

@@ -2764,20 +2764,21 @@ static void sculpt_pbvh_update_pixels(const Depsgraph &depsgraph,
/* -------------------------------------------------------------------- */
/** \name Generic Brush Plane & Symmetry Utilities
* \{ */
namespace blender::ed::sculpt_paint {
struct SculptRaycastData {
struct RaycastData {
Object *object;
SculptSession *ss;
const float *ray_start;
const float *ray_normal;
float3 ray_start;
float3 ray_normal;
bool hit;
float depth;
bool original;
Span<blender::float3> vert_positions;
blender::OffsetIndices<int> faces;
bool is_mid_stroke;
bool use_original;
Span<float3> vert_positions;
OffsetIndices<int> faces;
Span<int> corner_verts;
Span<blender::int3> corner_tris;
blender::VArraySpan<bool> hide_poly;
Span<int3> corner_tris;
VArraySpan<bool> hide_poly;
const SubdivCCG *subdiv_ccg;
@@ -2789,22 +2790,24 @@ struct SculptRaycastData {
IsectRayPrecalc isect_precalc;
};
struct SculptFindNearestToRayData {
struct FindNearestToRayData {
Object *object;
SculptSession *ss;
const float *ray_start, *ray_normal;
float3 ray_start;
float3 ray_normal;
bool hit;
float depth;
float dist_sq_to_ray;
bool original;
bool is_mid_stroke;
bool use_original;
Span<float3> vert_positions;
blender::OffsetIndices<int> faces;
OffsetIndices<int> faces;
Span<int> corner_verts;
Span<blender::int3> corner_tris;
blender::VArraySpan<bool> hide_poly;
Span<int3> corner_tris;
VArraySpan<bool> hide_poly;
const SubdivCCG *subdiv_ccg;
};
} // namespace blender::ed::sculpt_paint
ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3])
{
@@ -4031,7 +4034,7 @@ static void sculpt_update_cache_invariants(
/* Cache projection matrix. */
cache->projection_mat = ED_view3d_ob_project_mat_get(cache->vc->rv3d, &ob);
const float3 z_axis = {0.0f, 0.0f, 1.0f};
const float3 z_axis(0.0f, 0.0f, 1.0f);
ob.runtime->world_to_object = math::invert(ob.object_to_world());
cache->view_normal = math::normalize(math::transform_direction(
ob.world_to_object() * float4x4(cache->vc->rv3d->viewinv), z_axis));
@@ -4486,23 +4489,22 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object &ob, const Brush &b
}
}
static void sculpt_raycast_cb(blender::bke::pbvh::Node &node, SculptRaycastData &srd, float *tmin)
namespace blender::ed::sculpt_paint {
static void sculpt_raycast_cb(bke::pbvh::Node &node, RaycastData &rd, float *tmin)
{
using namespace blender;
using namespace blender::ed::sculpt_paint;
if (BKE_pbvh_node_get_tmin(&node) >= *tmin) {
return;
}
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*srd.object);
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*rd.object);
bool use_origco = false;
Span<float3> origco;
if (srd.original && srd.ss->cache) {
if (rd.use_original && rd.is_mid_stroke) {
switch (pbvh.type()) {
case bke::pbvh::Type::Mesh:
if (const std::optional<OrigPositionData> orig_data =
orig_position_data_lookup_mesh_all_verts(
*srd.object, static_cast<const bke::pbvh::MeshNode &>(node)))
*rd.object, static_cast<const bke::pbvh::MeshNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
@@ -4510,7 +4512,7 @@ static void sculpt_raycast_cb(blender::bke::pbvh::Node &node, SculptRaycastData
break;
case bke::pbvh::Type::Grids:
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_grids(
*srd.object, static_cast<const bke::pbvh::GridsNode &>(node)))
*rd.object, static_cast<const bke::pbvh::GridsNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
@@ -4532,82 +4534,80 @@ static void sculpt_raycast_cb(blender::bke::pbvh::Node &node, SculptRaycastData
int mesh_active_vert;
hit = bke::pbvh::node_raycast_mesh(static_cast<bke::pbvh::MeshNode &>(node),
origco,
srd.vert_positions,
srd.faces,
srd.corner_verts,
srd.corner_tris,
srd.hide_poly,
srd.ray_start,
srd.ray_normal,
&srd.isect_precalc,
&srd.depth,
rd.vert_positions,
rd.faces,
rd.corner_verts,
rd.corner_tris,
rd.hide_poly,
rd.ray_start,
rd.ray_normal,
&rd.isect_precalc,
&rd.depth,
mesh_active_vert,
srd.active_face_grid_index,
srd.face_normal);
rd.active_face_grid_index,
rd.face_normal);
if (hit) {
srd.active_vertex = mesh_active_vert;
rd.active_vertex = mesh_active_vert;
}
break;
}
case bke::pbvh::Type::Grids: {
SubdivCCGCoord grids_active_vert;
hit = bke::pbvh::node_raycast_grids(*srd.subdiv_ccg,
hit = bke::pbvh::node_raycast_grids(*rd.subdiv_ccg,
static_cast<bke::pbvh::GridsNode &>(node),
origco,
srd.ray_start,
srd.ray_normal,
&srd.isect_precalc,
&srd.depth,
rd.ray_start,
rd.ray_normal,
&rd.isect_precalc,
&rd.depth,
grids_active_vert,
srd.active_face_grid_index,
srd.face_normal);
rd.active_face_grid_index,
rd.face_normal);
if (hit) {
srd.active_vertex = grids_active_vert.to_index(
BKE_subdiv_ccg_key_top_level(*srd.subdiv_ccg));
rd.active_vertex = grids_active_vert.to_index(
BKE_subdiv_ccg_key_top_level(*rd.subdiv_ccg));
}
break;
}
case bke::pbvh::Type::BMesh: {
BMVert *bmesh_active_vert;
hit = bke::pbvh::node_raycast_bmesh(static_cast<bke::pbvh::BMeshNode &>(node),
srd.ray_start,
srd.ray_normal,
&srd.isect_precalc,
&srd.depth,
rd.ray_start,
rd.ray_normal,
&rd.isect_precalc,
&rd.depth,
use_origco,
&bmesh_active_vert,
srd.face_normal);
rd.face_normal);
if (hit) {
srd.active_vertex = bmesh_active_vert;
rd.active_vertex = bmesh_active_vert;
}
break;
}
}
if (hit) {
srd.hit = true;
*tmin = srd.depth;
rd.hit = true;
*tmin = rd.depth;
}
}
static void sculpt_find_nearest_to_ray_cb(blender::bke::pbvh::Node &node,
SculptFindNearestToRayData &srd,
static void sculpt_find_nearest_to_ray_cb(bke::pbvh::Node &node,
FindNearestToRayData &fntrd,
float *tmin)
{
using namespace blender;
using namespace blender::ed::sculpt_paint;
if (BKE_pbvh_node_get_tmin(&node) >= *tmin) {
return;
}
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*srd.object);
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*fntrd.object);
bool use_origco = false;
Span<float3> origco;
if (srd.original && srd.ss->cache) {
if (fntrd.use_original && fntrd.is_mid_stroke) {
switch (pbvh.type()) {
case bke::pbvh::Type::Mesh:
if (const std::optional<OrigPositionData> orig_data =
orig_position_data_lookup_mesh_all_verts(
*srd.object, static_cast<const bke::pbvh::MeshNode &>(node)))
*fntrd.object, static_cast<const bke::pbvh::MeshNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
@@ -4615,7 +4615,7 @@ static void sculpt_find_nearest_to_ray_cb(blender::bke::pbvh::Node &node,
break;
case bke::pbvh::Type::Grids:
if (const std::optional<OrigPositionData> orig_data = orig_position_data_lookup_grids(
*srd.object, static_cast<const bke::pbvh::GridsNode &>(node)))
*fntrd.object, static_cast<const bke::pbvh::GridsNode &>(node)))
{
use_origco = true;
origco = orig_data->positions;
@@ -4632,78 +4632,71 @@ static void sculpt_find_nearest_to_ray_cb(blender::bke::pbvh::Node &node,
node,
origco,
use_origco,
srd.vert_positions,
srd.faces,
srd.corner_verts,
srd.corner_tris,
srd.hide_poly,
srd.subdiv_ccg,
srd.ray_start,
srd.ray_normal,
&srd.depth,
&srd.dist_sq_to_ray))
fntrd.vert_positions,
fntrd.faces,
fntrd.corner_verts,
fntrd.corner_tris,
fntrd.hide_poly,
fntrd.subdiv_ccg,
fntrd.ray_start,
fntrd.ray_normal,
&fntrd.depth,
&fntrd.dist_sq_to_ray))
{
srd.hit = true;
*tmin = srd.dist_sq_to_ray;
fntrd.hit = true;
*tmin = fntrd.dist_sq_to_ray;
}
}
float SCULPT_raycast_init(ViewContext *vc,
const float mval[2],
float ray_start[3],
float ray_end[3],
float ray_normal[3],
bool original)
float raycast_init(ViewContext *vc,
const float2 &mval,
float3 &r_ray_start,
float3 &r_ray_end,
float3 &r_ray_normal,
bool original)
{
using namespace blender;
float obimat[4][4];
float dist;
Object &ob = *vc->obact;
RegionView3D *rv3d = vc->rv3d;
View3D *v3d = vc->v3d;
/* TODO: what if the segment is totally clipped? (return == 0). */
ED_view3d_win_to_segment_clipped(
vc->depsgraph, vc->region, vc->v3d, mval, ray_start, ray_end, true);
vc->depsgraph, vc->region, vc->v3d, mval, r_ray_start, r_ray_end, true);
invert_m4_m4(obimat, ob.object_to_world().ptr());
mul_m4_v3(obimat, ray_start);
mul_m4_v3(obimat, ray_end);
const float4x4 &world_to_object = ob.world_to_object();
r_ray_start = math::transform_point(world_to_object, r_ray_start);
r_ray_end = math::transform_point(world_to_object, r_ray_end);
sub_v3_v3v3(ray_normal, ray_end, ray_start);
dist = normalize_v3(ray_normal);
float dist;
r_ray_normal = math::normalize_and_get_length(r_ray_end - r_ray_start, dist);
/* If the ray is clipped, don't adjust its start/end. */
if ((rv3d->is_persp == false) && !RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
/* Get the view origin without the addition
* of -ray_normal * clip_start that
* ED_view3d_win_to_segment_clipped gave us.
* This is necessary to avoid floating point overflow.
*/
ED_view3d_win_to_origin(vc->region, mval, ray_start);
mul_m4_v3(obimat, ray_start);
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
bke::pbvh::clip_ray_ortho(pbvh, original, ray_start, ray_end, ray_normal);
dist = len_v3v3(ray_start, ray_end);
if (rv3d->is_persp || RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
return dist;
}
return dist;
/* Get the view origin without the addition
* of -ray_normal * clip_start that
* ED_view3d_win_to_segment_clipped gave us.
* This is necessary to avoid floating point overflow.
*/
float3 view_origin;
ED_view3d_win_to_origin(vc->region, mval, view_origin);
r_ray_start = math::transform_point(world_to_object, view_origin);
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
bke::pbvh::clip_ray_ortho(pbvh, original, r_ray_start, r_ray_end, r_ray_normal);
return math::distance(r_ray_start, r_ray_end);
}
bool SCULPT_cursor_geometry_info_update(bContext *C,
SculptCursorGeometryInfo *out,
const float mval[2],
bool use_sampled_normal)
bool cursor_geometry_info_update(bContext *C,
CursorGeometryInfo *out,
const float2 &mval,
const bool use_sampled_normal)
{
using namespace blender;
using namespace blender::ed::sculpt_paint;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Scene *scene = CTX_data_scene(C);
const Brush &brush = *BKE_paint_brush_for_read(BKE_paint_get_active_from_context(C));
float ray_start[3], ray_end[3], ray_normal[3], depth, mat[3][3];
float viewDir[3] = {0.0f, 0.0f, 1.0f};
bool original = false;
ViewContext vc = ED_view3d_viewcontext_init(C, depsgraph);
@@ -4717,21 +4710,24 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
bke::pbvh::Tree *pbvh = bke::object::pbvh_get(ob);
if (!pbvh || !vc.rv3d || !BKE_base_is_visible(v3d, base)) {
zero_v3(out->location);
zero_v3(out->normal);
zero_v3(out->active_vertex_co);
out->location = float3(0.0f);
out->normal = float3(0.0f);
out->active_vertex_co = float3(0.0f);
ss.clear_active_vert(false);
return false;
}
/* bke::pbvh::Tree raycast to get active vertex and face normal. */
depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original);
float3 ray_start;
float3 ray_end;
float3 ray_normal;
float depth = raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original);
SCULPT_stroke_modifiers_check(C, ob, brush);
SculptRaycastData srd{};
srd.original = original;
RaycastData srd{};
srd.use_original = original;
srd.object = &ob;
srd.ss = ob.sculpt;
srd.is_mid_stroke = ob.sculpt->cache != nullptr;
srd.hit = false;
if (pbvh->type() == bke::pbvh::Type::Mesh) {
const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
@@ -4756,13 +4752,13 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
[&](bke::pbvh::Node &node, float *tmin) { sculpt_raycast_cb(node, srd, tmin); },
ray_start,
ray_normal,
srd.original);
srd.use_original);
/* Cursor is not over the mesh, return default values. */
if (!srd.hit) {
zero_v3(out->location);
zero_v3(out->normal);
zero_v3(out->active_vertex_co);
out->location = float3(0.0f);
out->normal = float3(0.0f);
out->active_vertex_co = float3(0.0f);
ss.clear_active_vert(true);
return false;
}
@@ -4786,13 +4782,11 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
break;
}
copy_v3_v3(out->location, ray_normal);
mul_v3_fl(out->location, srd.depth);
add_v3_v3(out->location, ray_start);
out->location = ray_start + ray_normal * srd.depth;
/* Option to return the face normal directly for performance o accuracy reasons. */
if (!use_sampled_normal) {
copy_v3_v3(out->normal, srd.face_normal);
out->normal = srd.face_normal;
return srd.hit;
}
@@ -4800,14 +4794,12 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
float radius;
/* Update cursor data in SculptSession. */
invert_m4_m4(ob.runtime->world_to_object.ptr(), ob.object_to_world().ptr());
copy_m3_m4(mat, vc.rv3d->viewinv);
mul_m3_v3(mat, viewDir);
copy_m3_m4(mat, ob.world_to_object().ptr());
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);
const float3 z_axis = {0.0f, 0.0f, 1.0f};
ob.runtime->world_to_object = math::invert(ob.object_to_world());
ss.cursor_view_normal = math::normalize(
math::transform_direction(ob.world_to_object() * float4x4(vc.rv3d->viewinv), z_axis));
ss.cursor_normal = srd.face_normal;
ss.cursor_location = out->location;
ss.rv3d = vc.rv3d;
ss.v3d = vc.v3d;
@@ -4824,7 +4816,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
/* In case there are no nodes under the cursor, return the face normal. */
if (node_mask.is_empty()) {
copy_v3_v3(out->normal, srd.face_normal);
out->normal = srd.face_normal;
return true;
}
@@ -4834,38 +4826,28 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
if (const std::optional<float3> sampled_normal = calc_area_normal(
*depsgraph, brush, ob, node_mask))
{
copy_v3_v3(out->normal, *sampled_normal);
out->normal = *sampled_normal;
ss.cursor_sampled_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);
out->normal = srd.face_normal;
}
return true;
}
bool SCULPT_stroke_get_location(bContext *C,
float out[3],
const float mval[2],
bool force_original)
/**
* \param check_closest if true and the ray test fails a point closest to the ray will be found.
* \param limit_closest_radius if true then the closest point will be tested against the active
* brush radius. */
static bool stroke_get_location_bvh_ex(bContext *C,
float3 &out,
const float2 &mval,
const bool force_original,
const bool check_closest,
const bool limit_closest_radius)
{
const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
bool check_closest = brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE;
return SCULPT_stroke_get_location_ex(C, out, mval, force_original, check_closest, true);
}
bool SCULPT_stroke_get_location_ex(bContext *C,
float out[3],
const float mval[2],
bool force_original,
bool check_closest,
bool limit_closest_radius)
{
using namespace blender;
using namespace blender::ed::sculpt_paint;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
float ray_start[3], ray_end[3], ray_normal[3], depth;
ViewContext vc = ED_view3d_viewcontext_init(C, depsgraph);
@@ -4873,56 +4855,53 @@ bool SCULPT_stroke_get_location_ex(bContext *C,
SculptSession &ss = *ob.sculpt;
StrokeCache *cache = ss.cache;
bool original = force_original || ((cache) ? !cache->accum : false);
const bool original = force_original || ((cache) ? !cache->accum : false);
const Brush &brush = *BKE_paint_brush(BKE_paint_get_active_from_context(C));
SCULPT_stroke_modifiers_check(C, ob, brush);
depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original);
float3 ray_start;
float3 ray_end;
float3 ray_normal;
const float depth = raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original);
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
if (pbvh.type() == bke::pbvh::Type::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.object = &ob;
srd.ss = ob.sculpt;
srd.ray_start = ray_start;
srd.ray_normal = ray_normal;
srd.hit = false;
RaycastData rd;
rd.object = &ob;
rd.is_mid_stroke = ob.sculpt->cache != nullptr;
rd.ray_start = ray_start;
rd.ray_normal = ray_normal;
rd.hit = false;
if (pbvh.type() == bke::pbvh::Type::Mesh) {
const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
srd.vert_positions = bke::pbvh::vert_positions_eval(*depsgraph, ob);
srd.faces = mesh.faces();
srd.corner_verts = mesh.corner_verts();
srd.corner_tris = mesh.corner_tris();
rd.vert_positions = bke::pbvh::vert_positions_eval(*depsgraph, ob);
rd.faces = mesh.faces();
rd.corner_verts = mesh.corner_verts();
rd.corner_tris = mesh.corner_tris();
const bke::AttributeAccessor attributes = mesh.attributes();
srd.hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
rd.hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
}
else if (pbvh.type() == bke::pbvh::Type::Grids) {
srd.subdiv_ccg = ss.subdiv_ccg;
rd.subdiv_ccg = ss.subdiv_ccg;
}
SCULPT_vertex_random_access_ensure(ob);
srd.depth = depth;
srd.original = original;
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
rd.depth = depth;
rd.use_original = original;
isect_ray_tri_watertight_v3_precalc(&rd.isect_precalc, ray_normal);
bke::pbvh::raycast(
pbvh,
[&](bke::pbvh::Node &node, float *tmin) { sculpt_raycast_cb(node, srd, tmin); },
[&](bke::pbvh::Node &node, float *tmin) { sculpt_raycast_cb(node, rd, tmin); },
ray_start,
ray_normal,
srd.original);
if (srd.hit) {
rd.use_original);
if (rd.hit) {
hit = true;
copy_v3_v3(out, ray_normal);
mul_v3_fl(out, srd.depth);
add_v3_v3(out, ray_start);
out = ray_start + ray_normal * rd.depth;
}
}
@@ -4930,39 +4909,39 @@ bool SCULPT_stroke_get_location_ex(bContext *C,
return hit;
}
SculptFindNearestToRayData srd{};
srd.original = original;
srd.object = &ob;
srd.ss = ob.sculpt;
srd.hit = false;
FindNearestToRayData fntrd{};
fntrd.use_original = original;
fntrd.object = &ob;
fntrd.is_mid_stroke = ss.cache != nullptr;
fntrd.hit = false;
if (pbvh.type() == bke::pbvh::Type::Mesh) {
const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
srd.vert_positions = bke::pbvh::vert_positions_eval(*depsgraph, ob);
srd.faces = mesh.faces();
srd.corner_verts = mesh.corner_verts();
srd.corner_tris = mesh.corner_tris();
fntrd.vert_positions = bke::pbvh::vert_positions_eval(*depsgraph, ob);
fntrd.faces = mesh.faces();
fntrd.corner_verts = mesh.corner_verts();
fntrd.corner_tris = mesh.corner_tris();
const bke::AttributeAccessor attributes = mesh.attributes();
srd.hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
fntrd.hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
}
else if (pbvh.type() == bke::pbvh::Type::Grids) {
srd.subdiv_ccg = ss.subdiv_ccg;
fntrd.subdiv_ccg = ss.subdiv_ccg;
}
srd.ray_start = ray_start;
srd.ray_normal = ray_normal;
srd.depth = std::numeric_limits<float>::max();
srd.dist_sq_to_ray = std::numeric_limits<float>::max();
fntrd.ray_start = ray_start;
fntrd.ray_normal = ray_normal;
fntrd.depth = std::numeric_limits<float>::max();
fntrd.dist_sq_to_ray = std::numeric_limits<float>::max();
bke::pbvh::find_nearest_to_ray(
pbvh,
[&](bke::pbvh::Node &node, float *tmin) { sculpt_find_nearest_to_ray_cb(node, srd, tmin); },
[&](bke::pbvh::Node &node, float *tmin) {
sculpt_find_nearest_to_ray_cb(node, fntrd, tmin);
},
ray_start,
ray_normal,
srd.original);
if (srd.hit && srd.dist_sq_to_ray) {
fntrd.use_original);
if (fntrd.hit && fntrd.dist_sq_to_ray) {
hit = true;
copy_v3_v3(out, ray_normal);
mul_v3_fl(out, srd.depth);
add_v3_v3(out, ray_start);
out = ray_start + ray_normal * fntrd.depth;
}
float closest_radius_sq = std::numeric_limits<float>::max();
@@ -4971,9 +4950,27 @@ bool SCULPT_stroke_get_location_ex(bContext *C,
closest_radius_sq *= closest_radius_sq;
}
return hit && srd.dist_sq_to_ray < closest_radius_sq;
return hit && fntrd.dist_sq_to_ray < closest_radius_sq;
}
bool stroke_get_location_bvh(bContext *C,
float out[3],
const float mval[2],
const bool force_original)
{
const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
const bool check_closest = brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE;
float3 location;
const bool result = stroke_get_location_bvh_ex(
C, location, mval, force_original, check_closest, true);
if (result) {
copy_v3_v3(out, location);
}
return result;
}
} // namespace blender::ed::sculpt_paint
static void brush_init_tex(const Sculpt &sd, SculptSession &ss)
{
const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
@@ -5445,13 +5442,14 @@ void store_mesh_from_eval(const wmOperator &op,
* or over the background (0). */
static bool over_mesh(bContext *C, wmOperator * /*op*/, const float mval[2])
{
float co_dummy[3];
float3 co_dummy;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
bool check_closest = brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE;
const bool check_closest = brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE;
return SCULPT_stroke_get_location_ex(C, co_dummy, mval, false, check_closest, true);
return blender::ed::sculpt_paint::stroke_get_location_bvh_ex(
C, co_dummy, mval, false, check_closest, true);
}
static void stroke_undo_begin(const bContext *C, wmOperator *op)
@@ -5535,8 +5533,8 @@ static bool stroke_test_start(bContext *C, wmOperator *op, const float mval[2])
sculpt_update_cache_invariants(C, sd, ss, *op, mval);
SculptCursorGeometryInfo sgi;
SCULPT_cursor_geometry_info_update(C, &sgi, mval, false);
CursorGeometryInfo cgi;
cursor_geometry_info_update(C, &cgi, mval, false);
stroke_undo_begin(C, op);
@@ -5706,7 +5704,7 @@ static wmOperatorStatus sculpt_brush_stroke_invoke(bContext *C,
stroke = paint_stroke_new(C,
op,
SCULPT_stroke_get_location,
stroke_get_location_bvh,
stroke_test_start,
stroke_update_step,
nullptr,
@@ -5744,7 +5742,7 @@ static wmOperatorStatus sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
op->customdata = paint_stroke_new(C,
op,
SCULPT_stroke_get_location,
stroke_get_location_bvh,
stroke_test_start,
stroke_update_step,
nullptr,
@@ -6147,33 +6145,33 @@ void SCULPT_fake_neighbors_free(Object &ob)
pose_fake_neighbors_free(ss);
}
bool SCULPT_vertex_is_occluded(const Depsgraph &depsgraph,
const Object &object,
const float3 &position,
bool original)
namespace blender::ed::sculpt_paint {
bool vertex_is_occluded(const Depsgraph &depsgraph,
const Object &object,
const float3 &position,
bool original)
{
using namespace blender;
SculptSession &ss = *object.sculpt;
float ray_start[3], ray_end[3], ray_normal[3];
ViewContext *vc = ss.cache ? ss.cache->vc : &ss.filter_cache->vc;
const blender::float2 mouse = ED_view3d_project_float_v2_m4(
const float2 mouse = ED_view3d_project_float_v2_m4(
vc->region, position, ss.cache ? ss.cache->projection_mat : ss.filter_cache->viewmat);
int depth = SCULPT_raycast_init(vc, mouse, ray_end, ray_start, ray_normal, original);
float3 ray_start;
float3 ray_end;
float3 ray_normal;
float depth = raycast_init(vc, mouse, ray_end, ray_start, ray_normal, original);
negate_v3(ray_normal);
copy_v3_v3(ray_start, position);
madd_v3_v3fl(ray_start, ray_normal, 0.002);
ray_normal = ray_normal * -1.0f;
ray_start = position + ray_normal * 0.002f;
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(const_cast<Object &>(object));
SculptRaycastData srd = {nullptr};
srd.original = original;
RaycastData srd = {nullptr};
srd.use_original = original;
srd.object = &const_cast<Object &>(object);
srd.ss = &ss;
srd.is_mid_stroke = ss.cache != nullptr;
srd.hit = false;
srd.ray_start = ray_start;
srd.ray_normal = ray_normal;
@@ -6196,10 +6194,11 @@ bool SCULPT_vertex_is_occluded(const Depsgraph &depsgraph,
[&](bke::pbvh::Node &node, float *tmin) { sculpt_raycast_cb(node, srd, tmin); },
ray_start,
ray_normal,
srd.original);
srd.use_original);
return srd.hit;
}
} // namespace blender::ed::sculpt_paint
namespace blender::ed::sculpt_paint::islands {

View File

@@ -237,7 +237,7 @@ static bool calc_view_occlusion_factor(const Depsgraph &depsgraph,
const float3 &vert_position)
{
if (automasking.occlusion[vert] == Cache::OcclusionValue::Unknown) {
const bool occluded = SCULPT_vertex_is_occluded(depsgraph, object, vert_position, true);
const bool occluded = vertex_is_occluded(depsgraph, object, vert_position, true);
automasking.occlusion[vert] = occluded ? Cache::OcclusionValue::Occluded :
Cache::OcclusionValue::Visible;
}

View File

@@ -2415,8 +2415,8 @@ static wmOperatorStatus sculpt_cloth_filter_invoke(bContext *C,
/* Update the active vertex */
float2 mval_fl{float(event->mval[0]), float(event->mval[1])};
SculptCursorGeometryInfo sgi;
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
CursorGeometryInfo cgi;
cursor_geometry_info_update(C, &cgi, mval_fl, false);
/* Needs mask data to be available as it is used when solving the constraints. */
BKE_sculpt_update_object_for_edit(depsgraph, &ob, false);

View File

@@ -214,11 +214,11 @@ static bool sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2])
const bke::AttributeAccessor attributes = mesh.attributes();
const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
SculptCursorGeometryInfo sgi;
CursorGeometryInfo cgi;
/* Update the active vertex. */
const float mval_fl[2] = {float(mval[0]), float(mval[1])};
if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
if (!cursor_geometry_info_update(C, &cgi, mval_fl, false)) {
return false;
}
BKE_sculpt_update_object_for_edit(depsgraph, &ob, false);
@@ -259,9 +259,11 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2
SCULPT_stroke_modifiers_check(C, ob, brush);
const float mval_fl[2] = {float(mval[0]), float(mval[1])};
float ray_start[3], ray_end[3], ray_normal[3];
float depth = SCULPT_raycast_init(vc, mval_fl, ray_start, ray_end, ray_normal, false);
const float2 mval_fl = {float(mval[0]), float(mval[1])};
float3 ray_start;
float3 ray_end;
float3 ray_normal;
float depth = raycast_init(vc, mval_fl, ray_start, ray_end, ray_normal, false);
SculptDetailRaycastData srd;
srd.hit = false;

View File

@@ -2025,8 +2025,8 @@ static void update_for_vert(bContext *C, Object &ob, const std::optional<int> ve
static std::optional<int> target_vert_update_and_get(bContext *C, Object &ob, const float mval[2])
{
SculptSession &ss = *ob.sculpt;
SculptCursorGeometryInfo sgi;
if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) {
CursorGeometryInfo cgi;
if (cursor_geometry_info_update(C, &cgi, mval, false)) {
return ss.active_vert_index();
}
return std::nullopt;

View File

@@ -142,8 +142,8 @@ int active_update_and_get(bContext *C, Object &ob, const float mval[2])
return SCULPT_FACE_SET_NONE;
}
SculptCursorGeometryInfo gi;
if (!SCULPT_cursor_geometry_info_update(C, &gi, mval, false)) {
CursorGeometryInfo gi;
if (!cursor_geometry_info_update(C, &gi, mval, false)) {
return SCULPT_FACE_SET_NONE;
}
@@ -1101,10 +1101,10 @@ static wmOperatorStatus change_visibility_invoke(bContext *C, wmOperator *op, co
/* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
* cursor updates. */
SculptCursorGeometryInfo sgi;
CursorGeometryInfo cgi;
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
SCULPT_vertex_random_access_ensure(ob);
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
cursor_geometry_info_update(C, &cgi, mval_fl, false);
return change_visibility_exec(C, op);
}
@@ -1566,9 +1566,9 @@ static wmOperatorStatus edit_op_invoke(bContext *C, wmOperator *op, const wmEven
/* Update the current active Face Set and Vertex as the operator can be used directly from the
* tool without brush cursor. */
SculptCursorGeometryInfo sgi;
CursorGeometryInfo cgi;
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
if (!cursor_geometry_info_update(C, &cgi, mval_fl, false)) {
/* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
return OPERATOR_CANCELLED;
}

View File

@@ -478,8 +478,8 @@ static int sculpt_color_filter_init(bContext *C, wmOperator *op)
if (v3d) {
/* Update the active face set manually as the paint cursor is not enabled when using the Mesh
* Filter Tool. */
SculptCursorGeometryInfo sgi;
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
CursorGeometryInfo cgi;
cursor_geometry_info_update(C, &cgi, mval_fl, false);
}
}

View File

@@ -166,7 +166,7 @@ void cache_init(bContext *C,
float3 co;
if (vc.rv3d && SCULPT_stroke_get_location(C, co, mval_fl, false)) {
if (vc.rv3d && stroke_get_location_bvh(C, co, mval_fl, false)) {
/* Get radius from brush. */
const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
@@ -2424,8 +2424,8 @@ static wmOperatorStatus sculpt_mesh_filter_start(bContext *C, wmOperator *op)
if (use_automasking) {
/* Update the active face set manually as the paint cursor is not enabled when using the
* Mesh Filter Tool. */
SculptCursorGeometryInfo sgi;
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
CursorGeometryInfo cgi;
cursor_geometry_info_update(C, &cgi, mval_fl, false);
}
SCULPT_vertex_random_access_ensure(ob);

View File

@@ -125,12 +125,6 @@ enum class UpdateType {
} // namespace blender::ed::sculpt_paint
struct SculptCursorGeometryInfo {
blender::float3 location;
blender::float3 normal;
blender::float3 active_vertex_co;
};
/* Factor of brush to have rake point following behind
* (could be configurable but this is reasonable default). */
#define SCULPT_RAKE_BRUSH_FACTOR 0.25f
@@ -473,51 +467,48 @@ void SCULPT_tag_update_overlays(bContext *C);
/** \name Stroke Functions
* \{ */
namespace blender::ed::sculpt_paint {
/**
* Do a ray-cast 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.
*
* If check_closest is true and the ray test fails a point closest
* to the ray will be found. If limit_closest_radius is true then
* the closest point will be tested against the active brush radius.
* TODO: This should be updated to return std::optional<float3>
*/
bool SCULPT_stroke_get_location_ex(bContext *C,
float out[3],
const float mval[2],
bool force_original,
bool check_closest,
bool limit_closest_radius);
bool stroke_get_location_bvh(bContext *C, float out[3], const float mval[2], bool force_original);
struct CursorGeometryInfo {
float3 location;
float3 normal;
float3 active_vertex_co;
};
bool SCULPT_stroke_get_location(bContext *C,
float out[3],
const float mval[2],
bool force_original);
/**
* 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
*
* TODO: This should be updated to return `std::optional<CursorGeometryInfo>`
*/
bool SCULPT_cursor_geometry_info_update(bContext *C,
SculptCursorGeometryInfo *out,
const float mval[2],
bool use_sampled_normal);
namespace blender::ed::sculpt_paint {
bool cursor_geometry_info_update(bContext *C,
CursorGeometryInfo *out,
const float2 &mval,
bool use_sampled_normal);
void geometry_preview_lines_update(Depsgraph &depsgraph,
Object &object,
SculptSession &ss,
float radius);
}
} // namespace blender::ed::sculpt_paint
void SCULPT_stroke_modifiers_check(const bContext *C, Object &ob, const Brush &brush);
float SCULPT_raycast_init(ViewContext *vc,
const float mval[2],
float ray_start[3],
float ray_end[3],
float ray_normal[3],
bool original);
namespace blender::ed::sculpt_paint {
float raycast_init(ViewContext *vc,
const float2 &mval,
float3 &ray_start,
float3 &ray_end,
float3 &ray_normal,
bool original);
}
/* Symmetry */
ePaintSymmetryFlags SCULPT_mesh_symmetry_xyz_get(const Object &object);
@@ -561,12 +552,11 @@ void SCULPT_vertex_random_access_ensure(Object &object);
int SCULPT_vertex_count_get(const Object &object);
bool SCULPT_vertex_is_occluded(const Depsgraph &depsgraph,
const Object &object,
const blender::float3 &position,
bool original);
namespace blender::ed::sculpt_paint {
bool vertex_is_occluded(const Depsgraph &depsgraph,
const Object &object,
const float3 &position,
bool original);
/**
* Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled.

View File

@@ -840,8 +840,8 @@ static wmOperatorStatus mask_by_color(bContext *C, wmOperator *op, const float2
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
* so it needs to be updated here. */
SculptCursorGeometryInfo sgi;
SCULPT_cursor_geometry_info_update(C, &sgi, region_location, false);
CursorGeometryInfo cgi;
cursor_geometry_info_update(C, &cgi, region_location, false);
if (std::holds_alternative<std::monostate>(ss.active_vert())) {
return OPERATOR_CANCELLED;

View File

@@ -943,7 +943,7 @@ static wmOperatorStatus set_pivot_position_exec(bContext *C, wmOperator *op)
RNA_float_get(op->ptr, "mouse_x"),
RNA_float_get(op->ptr, "mouse_y"),
};
if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) {
if (stroke_get_location_bvh(C, stroke_location, mval, false)) {
copy_v3_v3(ss.pivot_pos, stroke_location);
}
}

View File

@@ -764,14 +764,14 @@ static void initialize_cursor_info(bContext &C,
int mval[2];
RNA_int_get_array(op.ptr, "location", mval);
SculptCursorGeometryInfo sgi;
CursorGeometryInfo cgi;
const float mval_fl[2] = {float(mval[0]), float(mval[1])};
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
trim_operation->initial_hit = SCULPT_cursor_geometry_info_update(&C, &sgi, mval_fl, false);
trim_operation->initial_hit = cursor_geometry_info_update(&C, &cgi, mval_fl, false);
if (trim_operation->initial_hit) {
copy_v3_v3(trim_operation->initial_location, sgi.location);
copy_v3_v3(trim_operation->initial_normal, sgi.normal);
copy_v3_v3(trim_operation->initial_location, cgi.location);
copy_v3_v3(trim_operation->initial_normal, cgi.normal);
}
}