Sculpt: Add Line Trim tool
This PR adds the *Line Trim* tool to Sculpt mode. It is exposed via the toolbar along with the other *Trim* tools and as an entry in the *Sculpt* menu. ## Technical Approach Line gestures are represented as two points in screen space: the `start` and `end` of the gesture. Trim tools work by taking a set of screen points as the top face of a volume, projecting a copy of the face into the scene, then using that as the operand shape for boolean operations. To behave as users would expect, the *Line Trim* tool does the following steps to make the initial face: 1. Take the sculpted object's bounding box. 2. Project the bounding box into screen space 3. Take the magnitude of the diagonal line made from the bounding box 4. Use the magnitude multiplied by an arbitrary factor to ensure the initial line is long enough to fully divide the object. 5. Create two points by moving in a perpendicular direction from start and end points. 6. Use the resulting four points as vertices of the quad in screen space. ## Differences with Other Trim Tools * Line Trim **only** supports the **Difference** mode. As such, the corresponding tool options have been disabled in the header. ## Alternatives * Instead of using a boolean operation, this could be achieved by using a bisect operation when using the *Fixed* projection mode. While this may result in a better performing tool, it is not guaranteed and requires extra work to integrate this approach. Pull Request: https://projects.blender.org/blender/blender/pulls/120845
This commit is contained in:
@@ -8131,6 +8131,16 @@ def km_3d_view_tool_sculpt_lasso_trim(params):
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_sculpt_line_trim(params):
|
||||
return (
|
||||
"3D View Tool: Sculpt, Line Trim",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("sculpt.trim_line_gesture", params.tool_maybe_tweak_event, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_sculpt_line_mask(params):
|
||||
return (
|
||||
"3D View Tool: Sculpt, Line Mask",
|
||||
@@ -9059,6 +9069,7 @@ def generate_keymaps(params=None):
|
||||
km_3d_view_tool_sculpt_lasso_face_set(params),
|
||||
km_3d_view_tool_sculpt_box_trim(params),
|
||||
km_3d_view_tool_sculpt_lasso_trim(params),
|
||||
km_3d_view_tool_sculpt_line_trim(params),
|
||||
km_3d_view_tool_sculpt_line_mask(params),
|
||||
km_3d_view_tool_sculpt_line_project(params),
|
||||
km_3d_view_tool_sculpt_mesh_filter(params),
|
||||
|
||||
@@ -1579,6 +1579,23 @@ class _defs_sculpt:
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def trim_line():
|
||||
def draw_settings(_context, layout, tool):
|
||||
props = tool.operator_properties("sculpt.trim_line_gesture")
|
||||
layout.prop(props, "trim_solver", expand=False)
|
||||
layout.prop(props, "trim_orientation", expand=False)
|
||||
layout.prop(props, "use_cursor_depth", expand=False)
|
||||
layout.prop(props, "use_limit_to_segment", expand=False)
|
||||
return dict(
|
||||
idname="builtin.line_trim",
|
||||
label="Line Trim",
|
||||
icon="ops.sculpt.line_trim",
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def project_line():
|
||||
def draw_settings(_context, layout, tool):
|
||||
@@ -3410,6 +3427,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
||||
(
|
||||
_defs_sculpt.trim_box,
|
||||
_defs_sculpt.trim_lasso,
|
||||
_defs_sculpt.trim_line,
|
||||
),
|
||||
_defs_sculpt.project_line,
|
||||
None,
|
||||
|
||||
@@ -3775,6 +3775,9 @@ class VIEW3D_MT_sculpt(Menu):
|
||||
props = layout.operator("sculpt.trim_lasso_gesture", text="Lasso Trim")
|
||||
props.trim_mode = 'DIFFERENCE'
|
||||
|
||||
props = layout.operator("sculpt.trim_line_gesture", text="Line Trim")
|
||||
props.trim_mode = 'DIFFERENCE'
|
||||
|
||||
props = layout.operator("sculpt.trim_box_gesture", text="Box Add")
|
||||
props.trim_mode = 'JOIN'
|
||||
|
||||
|
||||
@@ -235,6 +235,12 @@ std::unique_ptr<GestureData> init_from_line(bContext *C, wmOperator *op)
|
||||
line_points[1][0] = RNA_int_get(op->ptr, "xend");
|
||||
line_points[1][1] = RNA_int_get(op->ptr, "yend");
|
||||
|
||||
gesture_data->gesture_points.reinitialize(2);
|
||||
gesture_data->gesture_points[0][0] = line_points[0][0];
|
||||
gesture_data->gesture_points[0][1] = line_points[0][1];
|
||||
gesture_data->gesture_points[1][0] = line_points[1][0];
|
||||
gesture_data->gesture_points[1][1] = line_points[1][1];
|
||||
|
||||
gesture_data->line.flip = RNA_boolean_get(op->ptr, "flip");
|
||||
|
||||
float plane_points[4][3];
|
||||
|
||||
@@ -1787,6 +1787,7 @@ void SCULPT_OT_project_line_gesture(wmOperatorType *ot);
|
||||
namespace blender::ed::sculpt_paint::trim {
|
||||
void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot);
|
||||
void SCULPT_OT_trim_box_gesture(wmOperatorType *ot);
|
||||
void SCULPT_OT_trim_line_gesture(wmOperatorType *ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -1321,6 +1321,7 @@ void ED_operatortypes_sculpt()
|
||||
WM_operatortype_append(face_set::SCULPT_OT_face_set_box_gesture);
|
||||
WM_operatortype_append(trim::SCULPT_OT_trim_box_gesture);
|
||||
WM_operatortype_append(trim::SCULPT_OT_trim_lasso_gesture);
|
||||
WM_operatortype_append(trim::SCULPT_OT_trim_line_gesture);
|
||||
WM_operatortype_append(project::SCULPT_OT_project_line_gesture);
|
||||
|
||||
WM_operatortype_append(SCULPT_OT_sample_color);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_polyfill_2d.h"
|
||||
|
||||
#include "BKE_brush.hh"
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
@@ -274,13 +276,63 @@ static void calculate_depth(gesture::GestureData &gesture_data,
|
||||
r_depth_back = depth_back;
|
||||
}
|
||||
|
||||
/* Calculates a scalar factor to use to ensure a drawn line gesture
|
||||
* encompasses the entire object to be acted on. */
|
||||
static float calc_expand_factor(const gesture::GestureData &gesture_data)
|
||||
{
|
||||
Object *object = gesture_data.vc.obact;
|
||||
|
||||
rcti rect;
|
||||
const Bounds<float3> bounds = *BKE_object_boundbox_get(object);
|
||||
paint_convert_bb_to_rect(
|
||||
&rect, bounds.min, bounds.max, gesture_data.vc.region, gesture_data.vc.rv3d, object);
|
||||
|
||||
const float2 min_corner(rect.xmin, rect.ymin);
|
||||
const float2 max_corner(rect.xmax, rect.ymax);
|
||||
|
||||
/* Mutiply the screen space bounds by an arbitrary factor to ensure the created points are
|
||||
* sufficiently far and enclose the mesh to be operated on. */
|
||||
return math::distance(min_corner, max_corner) * 2.0f;
|
||||
}
|
||||
|
||||
/* Converts a line gesture's points into usable screen points. */
|
||||
static Array<float2> gesture_to_screen_points(gesture::GestureData &gesture_data)
|
||||
{
|
||||
if (gesture_data.shape_type != gesture::ShapeType::Line) {
|
||||
return gesture_data.gesture_points;
|
||||
}
|
||||
|
||||
const float expand_factor = calc_expand_factor(gesture_data);
|
||||
|
||||
float2 start(gesture_data.gesture_points[0]);
|
||||
float2 end(gesture_data.gesture_points[1]);
|
||||
|
||||
const float2 dir = math::normalize(end - start);
|
||||
|
||||
if (!gesture_data.line.use_side_planes) {
|
||||
end = end + dir * expand_factor;
|
||||
start = start - dir * expand_factor;
|
||||
}
|
||||
|
||||
float2 perp(dir.y, -dir.x);
|
||||
|
||||
if (gesture_data.line.flip) {
|
||||
perp *= -1;
|
||||
}
|
||||
|
||||
const float2 parallel_start = start + perp * expand_factor;
|
||||
const float2 parallel_end = end + perp * expand_factor;
|
||||
|
||||
return {start, end, parallel_end, parallel_start};
|
||||
}
|
||||
|
||||
static void generate_geometry(gesture::GestureData &gesture_data)
|
||||
{
|
||||
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
|
||||
ViewContext *vc = &gesture_data.vc;
|
||||
ARegion *region = vc->region;
|
||||
|
||||
const Span<float2> screen_points = gesture_data.gesture_points;
|
||||
const Array<float2> screen_points = gesture_to_screen_points(gesture_data);
|
||||
BLI_assert(screen_points.size() > 1);
|
||||
|
||||
const int trim_totverts = screen_points.size() * 2;
|
||||
@@ -614,6 +666,11 @@ static void init_operation(gesture::GestureData &gesture_data, wmOperator &op)
|
||||
if (!trim_operation->initial_hit) {
|
||||
trim_operation->orientation = OrientationType::View;
|
||||
}
|
||||
|
||||
if (gesture_data.shape_type == gesture::ShapeType::Line) {
|
||||
/* Line gestures only support Difference, no extrusion. */
|
||||
trim_operation->mode = OperationType::Difference;
|
||||
}
|
||||
}
|
||||
|
||||
static void operator_properties(wmOperatorType *ot)
|
||||
@@ -773,6 +830,37 @@ static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *even
|
||||
return WM_gesture_lasso_invoke(C, op, event);
|
||||
}
|
||||
|
||||
static int gesture_line_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
if (!can_exec(*C)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
|
||||
if (!gesture_data) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
gesture_data->operation = reinterpret_cast<gesture::Operation *>(
|
||||
MEM_cnew<TrimOperation>(__func__));
|
||||
|
||||
initialize_cursor_info(*C, *op, *gesture_data);
|
||||
init_operation(*gesture_data, *op);
|
||||
gesture::apply(*C, *gesture_data, *op);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (!can_invoke(*C)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
RNA_int_set_array(op->ptr, "location", event->mval);
|
||||
|
||||
return WM_gesture_straightline_active_side_invoke(C, op, event);
|
||||
}
|
||||
|
||||
void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Trim Lasso Gesture";
|
||||
@@ -814,4 +902,25 @@ void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
|
||||
|
||||
operator_properties(ot);
|
||||
}
|
||||
|
||||
void SCULPT_OT_trim_line_gesture(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Trim Line Gesture";
|
||||
ot->idname = "SCULPT_OT_trim_line_gesture";
|
||||
ot->description = "Trims the mesh divided by the line as you move the brush";
|
||||
|
||||
ot->invoke = gesture_line_invoke;
|
||||
ot->modal = WM_gesture_straightline_oneshot_modal;
|
||||
ot->exec = gesture_line_exec;
|
||||
|
||||
ot->poll = SCULPT_mode_poll_view3d;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER;
|
||||
|
||||
/* Properties. */
|
||||
WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
|
||||
gesture::operator_properties(ot, gesture::ShapeType::Line);
|
||||
|
||||
operator_properties(ot);
|
||||
}
|
||||
} // namespace blender::ed::sculpt_paint::trim
|
||||
|
||||
@@ -4135,6 +4135,7 @@ static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
|
||||
WM_modalkeymap_assign(keymap, "PAINT_OT_weight_gradient");
|
||||
WM_modalkeymap_assign(keymap, "MESH_OT_bisect");
|
||||
WM_modalkeymap_assign(keymap, "PAINT_OT_mask_line_gesture");
|
||||
WM_modalkeymap_assign(keymap, "SCULPT_OT_trim_line_gesture");
|
||||
WM_modalkeymap_assign(keymap, "SCULPT_OT_project_line_gesture");
|
||||
WM_modalkeymap_assign(keymap, "PAINT_OT_hide_show_line_gesture");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user