1
1

Compare commits

...

47 Commits

Author SHA1 Message Date
6485941d7a Merge branch 'master' into retopo_transform 2022-07-29 13:51:49 -04:00
eee25a175a Merge branch 'master' into retopo_transform 2022-07-27 14:31:45 -04:00
4f33dcff78 Merge branch 'active_modal_operator' into retopo_transform 2022-07-26 10:00:23 -04:00
fd39da1df6 Merge branch 'master' into retopo_transform 2022-07-26 10:00:14 -04:00
dbdab681cf Added operator to determine if operator is actively modal
Differential Revision: https://developer.blender.org/D15546
2022-07-26 09:53:25 -04:00
66c6cf0d71 Added operator to determine if operator is actively modal 2022-07-26 09:50:35 -04:00
8ab91edd91 Merge branch 'master' into retopo_transform 2022-07-26 00:25:37 -04:00
3ac5a52d6e Retopology Snapping Mode now working 2022-07-26 00:24:37 -04:00
60a8ade18a Merge branch 'master' into retopo_transform 2022-07-25 10:38:16 -04:00
d57ce54e30 UX-related tweaks 2022-07-25 10:35:38 -04:00
a735b2c335 Merge branch 'master' into retopo_transform 2022-07-22 11:41:35 -04:00
73aa6b8185 snap menu says "Tool" when retopo tool is active 2022-07-22 11:39:19 -04:00
698efac59e Merge branch 'master' into retopo_transform 2022-07-20 17:13:36 -04:00
afe11eff8a tweaked snap options and snap option descriptions
- retopo mode changes layout and options under snap menu
- retopo mode auto sets defaults on options not shown in menu
- reworked descriptions of snap options
2022-07-20 17:12:13 -04:00
4ebe1c3e69 Merge branch 'master' into retopo_transform 2022-07-18 12:13:45 -04:00
887713d08d using correct argument now 2022-07-16 10:30:45 -04:00
cc761cdae6 Merge branch 'master' into retopo_transform 2022-07-16 06:53:04 -04:00
a2938c86ca using new property name 2022-07-16 06:52:19 -04:00
a66e20f984 merged in master 2022-07-16 06:46:21 -04:00
db317f070e clean up before split 2022-07-07 15:09:23 -04:00
7502bc583c Merge branch 'master' into transform_api 2022-07-07 14:51:22 -04:00
089870ab3a reorg+cleaned snap menu, removed dbg prints, reorg edge snap methods 2022-07-07 14:50:26 -04:00
298711d158 Merge branch 'master' into transform_api 2022-07-07 10:30:30 -04:00
8d284d4854 merged in master 2022-07-05 17:01:42 -04:00
9e88cfbe0c added retopo mode, updated transform ops 2022-06-28 11:46:00 -04:00
405bbb06f2 Merge branch 'D15154-gizmogroup_fallback' into transform_api 2022-06-08 10:38:29 -04:00
ed8f2bbf5c Merge branch 'D15153-cursor_relative' into transform_api 2022-06-08 10:38:14 -04:00
ddce8e9ea3 Merge branch 'master' into transform_api 2022-06-08 10:37:56 -04:00
1b9e31f004 exposed fallback option to Gizmo type
Differential Revision: https://developer.blender.org/D15154
2022-06-08 10:17:29 -04:00
8791762af0 exposed cursor_warp_relative through api
Differential Revision: https://developer.blender.org/D15153
2022-06-08 10:08:41 -04:00
8d813f2eed added snapping options to transform api 2022-06-08 09:45:03 -04:00
af29d103c6 sync icons 2022-06-08 09:42:57 -04:00
ca336c600b revert transform API changes (moved to another patch) 2022-06-07 16:25:15 -04:00
491ada0a38 revert some transform_ops.c 2022-06-07 16:13:04 -04:00
62f813754d minor change to capitalization of label 2022-06-07 16:10:26 -04:00
cbeb70bdae replaced static_cast with enum operator 2022-06-07 12:09:04 -04:00
139a651434 explicit tests against 0 rather than implicit bool coversion 2022-06-07 11:56:06 -04:00
add307d429 added comment 2022-06-07 11:46:22 -04:00
59e6dc8a93 minor variable name change 2022-06-07 11:44:56 -04:00
6410fe0492 improved comments 2022-06-07 11:22:55 -04:00
b818008ddf addressed reviewer comments, updated versioning (untested) 2022-06-07 11:12:28 -04:00
af9c969768 Merge branch 'master' into D14591-transform_snap_nearest 2022-06-07 09:32:37 -04:00
0b25d923e5 use face raycast initially with face nearest as fallback 2022-06-07 09:31:48 -04:00
a863ba191d Merge branch 'master' into D14591-transform_snap_nearest_old 2022-06-06 17:11:32 -04:00
f606393522 sync with work on laptop 2022-06-03 11:53:52 -04:00
f8b389b121 Merge branch 'master' into arcpatch-D14591 2022-06-03 11:53:34 -04:00
Jon Denning
59adee83e7 Transform Snap: added nearest face snap mode, added snapping options, lightly refactored snapping code.
This diff adds a new face nearest snapping mode, adds new snapping options, and (lightly) refactors code around snapping.

The new face nearest snapping mode will snap transformed geometry to the nearest surface in world space.  In contrast, the original face snapping mode uses projection (raycasting) to snap source to target geometry.  Face snapping therefore only works with what is visible, while nearest face snapping can snap geometry to occluded parts of the scene.  This new mode is critical for retopology work, where some of the target mesh might be occluded.

The nearest face snapping mode has two options: "Snap to Same Target" and "Face Nearest Steps".  When the Snap to Same Object option is enabled, the selected source geometry will stay near the target that it is nearest before editing started, which prevents the source geometry from snapping to other targets.  The Face Nearest Steps divides the overall transformation for each vertex into `n` smaller transformations, then applies those `n` transformations with surface snapping interlacing each step.  This steps option handles transformations that cross U-shaped objects better.

The new snapping options allow the artist to better control which target objects (objects to which the edited geometry is snapped) are considered when snapping.  In particular, the only option for filtering target objects was a "Project onto Self", which allowed the currently edited mesh to be considered as a target.  Now, the artist can choose any combination of the following to be considered as a target: the active object, any edited object that isn't active (see note below), any non-edited object.  Additionally, the artist has another snapping option to exclude objects that are not selectable as potential targets.

The Snapping Options dropdown has been lightly reorganized to allow for the additional options.

Included in this patch:

  - Refactored the snap-related `#define`s into `enum`s, and refactored enum-related `char`, `short`, and `int` to use the appropriate enum instead.
  - Snap target selection is more controllable for artist with additional snapping options.
  - Renamed a few of the snap-related functions to better reflect what they actually do now.  For example, `applySnapping` implies that this handles the snapping, while `applyProject` implies something entirely different is done there.  However, better names would be `applySnappingAsGroup` and `applySnappingIndividual`, respectively, where `applySnappingIndividual` previously only does Face snapping.
  - Added an initial coordinate parameter to snapping functions so that the nearest target before transforming can be determined (for "Snap to Same Object"), and so the transformation can be broken into smaller steps (for "Face Nearest Steps").
  - Separated the BVH Tree getter code from mesh/edit mesh to its own function to reduce code duplication.
  - Added icon for nearest face snapping.
  - Updated `startup.blend` so face nearest steps starts at 1, and the snap target selection options have reasonable defaults (include self, include edited, include nonedited)
  - The original "Project onto Self" was actually not correct!  This option should be called "Project onto Active" instead, but that only matters when editing multiple meshes at the same time.  This patch makes this change.

Not included in this patch / future updates:

  - Snapping "Target" is a confusing named, as "Target" is used as both the transformed items (or `SCE_SNAP_TARGET_CLOSEST`, etc.) and for the objects to which the transformed items are snapped (especially Shrinkwrap modifier).  I plan to submit another patch to make this clearer after this is accepted.
  - Many of the functions do not specify in which space the point info (coordinates and normal) is defined.
  - Target selection code could be simplified by separating it from the uber `snap_flag` variable.
  - The snapping dropdown is feeling very disorganized.  Also, since enabling both the Face Projection and the Face Nearest methods does not make sense, perhaps the switch between these methods could be a checkbox (similar to snapping to relative or absolute grid).

Differential Revision: https://developer.blender.org/D14591
2022-06-03 10:48:04 -04:00
14 changed files with 325 additions and 124 deletions

Binary file not shown.

View File

@@ -619,7 +619,11 @@ class VIEW3D_HT_header(Header):
if show_snap:
snap_items = bpy.types.ToolSettings.bl_rna.properties["snap_elements"].enum_items
snap_elements = tool_settings.snap_elements
if len(snap_elements) == 1:
active_tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False)
if active_tool.idname.startswith('retopology'):
text = "Tool"
icon = 'NONE'
elif len(snap_elements) == 1:
text = ""
for elem in snap_elements:
icon = snap_items[elem].icon
@@ -632,6 +636,14 @@ class VIEW3D_HT_header(Header):
row = layout.row(align=True)
row.prop(tool_settings, "use_snap", text="")
if object_mode == 'EDIT' and obj.type == 'MESH':
row.prop(
tool_settings,
"use_snap_retopology_mode",
text="",
icon='MOD_MESHDEFORM',
)
sub = row.row(align=True)
sub.popover(
panel="VIEW3D_PT_snapping",
@@ -6808,73 +6820,106 @@ class VIEW3D_PT_snapping(Panel):
tool_settings = context.tool_settings
snap_elements = tool_settings.snap_elements
obj = context.active_object
object_mode = 'OBJECT' if obj is None else obj.mode
object_mode = obj.mode if obj else 'OBJECT'
show_target_options = object_mode == 'EDIT' and obj.type not in {'LATTICE', 'META', 'FONT'}
multiple_objects = len(context.objects_in_mode) > 1
retopo_mode = object_mode == 'EDIT' and tool_settings.use_snap_retopology_mode
layout = self.layout
col = layout.column()
col.label(text="Snap To")
col.prop(tool_settings, "snap_elements", expand=True)
col.separator()
if 'INCREMENT' in snap_elements:
col.prop(tool_settings, "use_snap_grid_absolute")
if show_target_options:
col.prop(
tool_settings,
"use_snap_retopology_mode",
text="Retopology Mode",
# icon='MOD_MESHDEFORM',
)
if not retopo_mode:
col_snapto = col.column(align=True, heading="Snap To")
col_snapto.prop(tool_settings, "snap_elements", expand=True)
else:
col_snapto_edited = col.column(align=True, heading="Snap To Edited")
col_snapto_edited.prop_enum(tool_settings, "snap_elements", 'VERTEX')
col_snapto_edited.prop_enum(tool_settings, "snap_elements", 'EDGE')
col_snapto_edited.prop_enum(tool_settings, "snap_elements", 'EDGE_MIDPOINT')
col_snapto_edited.prop_enum(tool_settings, "snap_elements", 'EDGE_PERPENDICULAR')
col_snapto_nonedited = col.column(align=True, heading="Snap To Non-Edited")
col_snapto_nonedited.prop_enum(tool_settings, "snap_elements", 'FACE')
col_snapto_nonedited.prop_enum(tool_settings, "snap_elements", 'FACE_NEAREST')
# row = col_snapto_nonedited.row(align=True)
# row.enabled = False
# row.prop_enum(tool_settings, "snap_elements", 'FACE_NEAREST')
if not retopo_mode and snap_elements - {'INCREMENT', 'FACE_NEAREST'}:
col_snapwith = col.column(align=True)
col_snapwith.label(text='Snap With')
col_snapwith.row().prop(tool_settings, "snap_target", expand=True)
if snap_elements != {'INCREMENT'}:
if snap_elements != {'FACE_NEAREST'}:
col.label(text="Snap With")
row = col.row(align=True)
row.prop(tool_settings, "snap_target", expand=True)
if obj:
col.label(text="Target Selection")
col_targetsel = col.column(align=True)
if object_mode == 'EDIT' and obj.type not in {'LATTICE', 'META', 'FONT'}:
col_targetsel = col.column(align=True, heading="Target Selection")
if not retopo_mode and show_target_options:
if not multiple_objects:
col_targetsel.prop(
tool_settings,
'use_snap_self',
text="Include Edited",
# description='Snap onto edited objects (Edit Mode Only)',
icon='EDITMODE_HLT',
)
else:
col_targetsel.prop(
tool_settings,
"use_snap_self",
text="Include Active",
text="Include Active Edited",
# description='Snap onto active edited object (Edit Mode Only)',
icon='EDITMODE_HLT',
)
col_targetsel.prop(
tool_settings,
"use_snap_edit",
text="Include Edited",
text="Include Other Edited",
# description='Snap onto non-active edited object(s) (Edit Mode Only)',
icon='OUTLINER_DATA_MESH',
)
col_targetsel.prop(
tool_settings,
"use_snap_nonedit",
text="Include Non-Edited",
icon='OUTLINER_OB_MESH',
)
col_targetsel.prop(
tool_settings,
"use_snap_selectable",
text="Exclude Non-Selectable",
icon='RESTRICT_SELECT_OFF',
"use_snap_nonedit",
text="Include Non-Edited",
icon='OUTLINER_OB_MESH',
)
col_targetsel.prop(
tool_settings,
"use_snap_selectable_only",
text="Exclude Non-Selectable",
icon='RESTRICT_SELECT_OFF',
)
if object_mode in {'OBJECT', 'POSE', 'EDIT', 'WEIGHT_PAINT'}:
col.prop(tool_settings, "use_snap_align_rotation")
col.prop(tool_settings, "use_snap_backface_culling")
if 'FACE' in snap_elements:
col.prop(tool_settings, "use_snap_project")
col_options = col.column(heading="Options")
if 'INCREMENT' in snap_elements:
col_options.prop(tool_settings, "use_snap_grid_absolute")
if snap_elements != {'INCREMENT'}:
# TODO(@gfxcoder): Does WEIGHT_PAINT have any snapping?
if object_mode in {'OBJECT', 'POSE', 'EDIT', 'WEIGHT_PAINT'}:
col_options.prop(tool_settings, "use_snap_align_rotation")
if snap_elements != {'FACE_NEAREST'}:
col_options.prop(tool_settings, "use_snap_backface_culling")
if 'FACE' in snap_elements and not retopo_mode:
col_options.prop(tool_settings, "use_snap_project")
if 'FACE_NEAREST' in snap_elements:
col.prop(tool_settings, 'use_snap_to_same_target')
col_options.prop(tool_settings, 'use_snap_to_same_target')
if object_mode == 'EDIT':
col.prop(tool_settings, 'snap_face_nearest_steps')
col_options.prop(tool_settings, 'snap_face_nearest_steps')
if 'VOLUME' in snap_elements:
col.prop(tool_settings, "use_snap_peel_object")
col_options.prop(tool_settings, "use_snap_peel_object")
col.label(text="Affect")
row = col.row(align=True)
row.prop(tool_settings, "use_snap_translate", text="Move", toggle=True)
row.prop(tool_settings, "use_snap_rotate", text="Rotate", toggle=True)
row.prop(tool_settings, "use_snap_scale", text="Scale", toggle=True)
if not retopo_mode:
row = col.row(align=True, heading="Affect")
row.prop(tool_settings, "use_snap_translate", text="Move", toggle=True)
row.prop(tool_settings, "use_snap_rotate", text="Rotate", toggle=True)
row.prop(tool_settings, "use_snap_scale", text="Scale", toggle=True)
class VIEW3D_PT_proportional_edit(Panel):

View File

@@ -26,7 +26,6 @@ struct View3D;
/* transform_snap_object.cc */
/* ED_transform_snap_object_*** API */
typedef enum eSnapEditType {
SNAP_GEOM_FINAL = 0,
SNAP_GEOM_CAGE = 1,

View File

@@ -655,6 +655,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
}
if (flags & P_SNAP) {
// TODO: rename `snap` to `use_snap`?
prop = RNA_def_boolean(ot->srna, "snap", false, "Use Snapping Options", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
@@ -686,6 +687,12 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
prop = RNA_def_boolean(
ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_boolean(ot->srna,
"use_snap_retopology_mode",
true,
"Target: Retopology Mode",
"Optimize snapping options for retopology work");
RNA_def_property_flag(prop, PROP_HIDDEN);
/* Face Nearest options */
prop = RNA_def_boolean(

View File

@@ -128,11 +128,11 @@ bool activeSnap(const TransInfo *t)
bool activeSnap_SnappingIndividual(const TransInfo *t)
{
if (activeSnap(t) && t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST) {
if (activeSnap(t) && (t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST)) {
return true;
}
if (!t->tsnap.project) {
if ((t->tsnap.mode & SCE_SNAP_MODE_FACE_RAYCAST) && !t->tsnap.project) {
return false;
}
@@ -168,9 +168,22 @@ bool activeSnap_SnappingAsGroup(const TransInfo *t)
return true;
}
static bool useSnapRetopoMode(const TransInfo *t)
{
if (t->spacetype != SPACE_VIEW3D || t->obedit_type != OB_MESH) {
/* Not the correct context for retopology mode. */
return false;
}
return t->settings->snap_flag & SCE_SNAP_RETOPOLOGY_MODE;
}
bool transformModeUseSnap(const TransInfo *t)
{
ToolSettings *ts = t->settings;
if (useSnapRetopoMode(t)) {
/* Always use snap when retopology mode is enabled. */
return true;
}
if (t->mode == TFM_TRANSLATION) {
return (ts->snap_transform_mode_flag & SCE_SNAP_TRANSFORM_MODE_TRANSLATE) != 0;
}
@@ -509,9 +522,13 @@ void applySnappingIndividual(TransInfo *t)
continue;
}
/* If both face ray-cast and face nearest methods are enabled, start with face ray-cast and
* fallback to face nearest ray-cast does not hit. */
bool hit = applyFaceProject(t, tc, td);
/* If both face raycast and face nearest methods are enabled, start with face raycast and
* fallback to face nearest raycast does not hit. */
bool hit = false;
if (t->tsnap.flag & SCE_SNAP_PROJECT) {
/* Only raycast in this function if projecting individual elements. */
hit = applyFaceProject(t, tc, td);
}
if (!hit) {
applyFaceNearest(t, tc, td);
}
@@ -738,6 +755,7 @@ static eSnapTargetSelect snap_target_select_from_spacetype(TransInfo *t)
bool use_snap_edit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_EDITED) == 0;
bool use_snap_nonedit = (t->tsnap.target_select & SCE_SNAP_TARGET_NOT_NONEDITED) == 0;
bool use_snap_selectable_only = (t->tsnap.target_select & SCE_SNAP_TARGET_ONLY_SELECTABLE) != 0;
// bool use_retopology_mode = (t->tsnap.target_select & SCE_SNAP_TARGET_RETOPOLOGY_MODE) != 0;
if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) {
if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
@@ -840,6 +858,25 @@ static void initSnappingMode(TransInfo *t)
}
}
static void initSnappingRetopoMode(TransInfo *t)
{
if (!useSnapRetopoMode(t)) {
/* Not using retopology mode. */
return;
}
/* Enable all possible targets. The targets will be filtered based on snap method. */
t->tsnap.source_select = SCE_SNAP_SOURCE_ACTIVE;
t->tsnap.targetSnap = TargetSnapActive;
t->tsnap.target_select &= ~(SCE_SNAP_TARGET_NOT_ACTIVE | SCE_SNAP_TARGET_NOT_EDITED |
SCE_SNAP_TARGET_NOT_NONEDITED);
t->tsnap.flag |= (SCE_SNAP_TO_INCLUDE_EDITED | SCE_SNAP_TO_INCLUDE_NONEDITED);
t->tsnap.flag &= ~(SCE_SNAP_NOT_TO_ACTIVE);
t->tsnap.mode &= ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_VOLUME);
t->tsnap.project = true;
t->tsnap.flag |= SCE_SNAP_PROJECT;
}
void initSnapping(TransInfo *t, wmOperator *op)
{
ToolSettings *ts = t->settings;
@@ -912,12 +949,19 @@ void initSnapping(TransInfo *t, wmOperator *op)
SCE_SNAP_TARGET_NOT_NONEDITED);
}
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) &&
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable_only")) &&
RNA_property_is_set(op->ptr, prop)) {
SET_FLAG_FROM_TEST(t->tsnap.target_select,
RNA_property_boolean_get(op->ptr, prop),
SCE_SNAP_TARGET_ONLY_SELECTABLE);
}
if ((prop = RNA_struct_find_property(op->ptr, "use_snap_retopology_mode")) &&
RNA_property_is_set(op->ptr, prop)) {
SET_FLAG_FROM_TEST(t->tsnap.target_select,
RNA_property_boolean_get(op->ptr, prop),
SCE_SNAP_TARGET_RETOPOLOGY_MODE);
}
}
}
/* use scene defaults only when transform is modal */
@@ -926,9 +970,10 @@ void initSnapping(TransInfo *t, wmOperator *op)
t->modifiers |= MOD_SNAP;
}
t->tsnap.target_select = SCE_SNAP_TARGET_ALL;
t->tsnap.align = ((t->tsnap.flag & SCE_SNAP_ROTATE) != 0);
t->tsnap.project = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0);
t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0);
t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PEEL_OBJECT) != 0);
SET_FLAG_FROM_TEST(t->tsnap.target_select,
(ts->snap_flag & SCE_SNAP_NOT_TO_ACTIVE),
SCE_SNAP_TARGET_NOT_ACTIVE);
@@ -941,11 +986,15 @@ void initSnapping(TransInfo *t, wmOperator *op)
SET_FLAG_FROM_TEST(t->tsnap.target_select,
(ts->snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE),
SCE_SNAP_TARGET_ONLY_SELECTABLE);
SET_FLAG_FROM_TEST(t->tsnap.target_select,
(t->settings->snap_flag & SCE_SNAP_RETOPOLOGY_MODE),
SCE_SNAP_TARGET_RETOPOLOGY_MODE);
}
t->tsnap.source_select = snap_source;
initSnappingMode(t);
initSnappingRetopoMode(t);
}
void freeSnapping(TransInfo *t)
@@ -1446,16 +1495,28 @@ eSnapMode snapObjectsTransform(
TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
{
float *target = (t->tsnap.status & TARGET_INIT) ? t->tsnap.snapTarget : t->center_global;
// filter mode to group snapping modes
eSnapMode mode = t->tsnap.mode;
mode &= ~(SCE_SNAP_MODE_FACE_NEAREST);
if (t->tsnap.flag & SCE_SNAP_PROJECT) {
mode &= ~(SCE_SNAP_MODE_FACE_RAYCAST);
}
const bool use_retopo_mode = (t->tsnap.target_select & SCE_SNAP_TARGET_RETOPOLOGY_MODE);
const bool face_raycast_only = t->settings->snap_mode == SCE_SNAP_MODE_FACE_RAYCAST;
const bool use_occlusion_test = use_retopo_mode || !face_raycast_only;
return ED_transform_snap_object_project_view3d(
t->tsnap.object_context,
t->depsgraph,
t->region,
t->view,
t->tsnap.mode,
mode,
&(const struct SnapObjectParams){
.snap_target_select = t->tsnap.target_select,
.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE_RAYCAST,
.use_occlusion_test = use_occlusion_test,
.use_backface_culling = t->tsnap.use_backface_culling,
},
NULL,

View File

@@ -1025,6 +1025,12 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
/* read/write args */
float *ray_depth = dt->ray_depth;
const bool use_retopo_mode = params->snap_target_select & SCE_SNAP_TARGET_RETOPOLOGY_MODE;
const bool is_object_edited = BKE_object_is_in_editmode(ob_eval);
if (use_retopo_mode && is_object_edited) {
return;
}
bool retval = false;
if (use_occlusion_test) {
if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) {
@@ -1261,7 +1267,7 @@ static bool nearest_world_tree(SnapObjectContext *UNUSED(sctx),
/* compute offset between init co and prev co in local space */
float init_co_local[3], curr_co_local[3];
float delta_local[3];
float delta_local[3], delta_step[3];
mul_v3_m4v3(init_co_local, imat, init_co);
mul_v3_m4v3(curr_co_local, imat, curr_co);
sub_v3_v3v3(delta_local, curr_co_local, init_co_local);
@@ -1273,8 +1279,8 @@ static bool nearest_world_tree(SnapObjectContext *UNUSED(sctx),
}
else {
/* NOTE: when `params->face_nearest_steps == 1`, the return variables of function below contain
* the answer. We could return immediately after updating r_loc, r_no, r_index, but that would
* also complicate the code. Foregoing slight optimization for code clarity. */
* the answer. We could return immediately after updating 'r_loc', 'r_no', 'r_index', but that
* would also complicate the code. Foregoing slight optimization for code clarity. */
nearest_world_tree_co(
tree, nearest_cb, treedata, curr_co_local, nullptr, nullptr, nullptr, &dist_sq);
}
@@ -1284,8 +1290,9 @@ static bool nearest_world_tree(SnapObjectContext *UNUSED(sctx),
*r_dist_sq = dist_sq;
/* scale to make `snap_face_nearest_steps` steps */
float step_scale_factor = 1.0f / max_ff(1.0f, (float)params->face_nearest_steps);
mul_v3_fl(delta_local, step_scale_factor);
int steps = max_ii(1, params->face_nearest_steps);
float factor = 1.0f / (float)steps;
mul_v3_v3fl(delta_step, delta_local, factor);
float co_local[3];
float no_local[3];
@@ -1293,8 +1300,8 @@ static bool nearest_world_tree(SnapObjectContext *UNUSED(sctx),
copy_v3_v3(co_local, init_co_local);
for (int i = 0; i < params->face_nearest_steps; i++) {
add_v3_v3(co_local, delta_local);
for (int i = 0; i < steps; i++) {
add_v3_v3(co_local, delta_step);
nearest_world_tree_co(
tree, nearest_cb, treedata, co_local, co_local, no_local, &index, nullptr);
}
@@ -1384,6 +1391,12 @@ static void nearest_world_object_fn(SnapObjectContext *sctx,
{
struct NearestWorldObjUserData *dt = static_cast<NearestWorldObjUserData *>(data);
const bool use_retopo_mode = params->snap_target_select & SCE_SNAP_TARGET_RETOPOLOGY_MODE;
const bool is_object_edited = BKE_object_is_in_editmode(ob_eval);
if (use_retopo_mode && is_object_edited) {
return;
}
bool retval = false;
switch (ob_eval->type) {
case OB_MESH: {
@@ -3055,6 +3068,12 @@ static void snap_obj_fn(SnapObjectContext *sctx,
SnapObjUserData *dt = static_cast<SnapObjUserData *>(data);
eSnapMode retval = SCE_SNAP_MODE_NONE;
const bool use_retopo_mode = (params->snap_target_select & SCE_SNAP_TARGET_RETOPOLOGY_MODE);
const bool is_object_edited = BKE_object_is_in_editmode(ob_eval);
if (use_retopo_mode && !is_object_edited) {
return;
}
switch (ob_eval->type) {
case OB_MESH: {
const eSnapEditType edit_mode_type = params->edit_mode_type;
@@ -3384,47 +3403,29 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
sctx->runtime.region = region;
sctx->runtime.v3d = v3d;
BLI_assert((snap_to_flag & SCE_SNAP_MODE_GEOM) != 0);
if ((snap_to_flag & SCE_SNAP_MODE_GEOM) == 0) {
return SCE_SNAP_MODE_NONE;
}
// BLI_assert((snap_to_flag & SCE_SNAP_MODE_GEOM) != 0);
eSnapMode retval = SCE_SNAP_MODE_NONE;
bool has_hit = false;
Object *ob_eval = nullptr;
Object *ob_ray = nullptr;
float loc[3];
/* Not all snapping callbacks set the normal,
* initialize this since any hit copies both the `loc` and `no`. */
float no[3] = {0.0f, 0.0f, 0.0f};
float obmat[4][4];
float obmat_ray[4][4];
int index = -1;
const RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d);
/* Note: if both face raycast and face nearest are enabled, first find result of nearest, then
* override with raycast. */
if ((snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST) && !has_hit) {
has_hit = nearestWorldObjects(
sctx, params, init_co, prev_co, loc, no, &index, &ob_eval, obmat);
if (has_hit) {
retval = SCE_SNAP_MODE_FACE_NEAREST;
copy_v3_v3(r_loc, loc);
if (r_no) {
copy_v3_v3(r_no, no);
}
if (r_ob) {
*r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
}
if (r_index) {
*r_index = index;
}
}
}
const bool use_retopo_mode = params->snap_target_select & SCE_SNAP_TARGET_RETOPOLOGY_MODE;
const bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d);
if (snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST || use_occlusion_test) {
float ray_start[3], ray_normal[3];
@@ -3443,8 +3444,8 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
loc,
no,
&index,
&ob_eval,
obmat,
&ob_ray, // &ob_eval,
obmat_ray, // obmat,
nullptr);
if (has_hit) {
@@ -3453,6 +3454,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
}
if ((snap_to_flag & SCE_SNAP_MODE_FACE_RAYCAST)) {
/* Record snap results only if face raycast snapping mode is enabled. */
retval = SCE_SNAP_MODE_FACE_RAYCAST;
copy_v3_v3(r_loc, loc);
@@ -3460,15 +3462,19 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
copy_v3_v3(r_no, no);
}
if (r_ob) {
*r_ob = ob_eval;
*r_ob = ob_ray; // ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
copy_m4_m4(r_obmat, obmat_ray); // obmat
}
if (r_index) {
*r_index = index;
}
}
if (use_occlusion_test && !use_retopo_mode) {
ob_eval = ob_ray;
copy_m4_m4(obmat, obmat_ray);
}
}
}
@@ -3503,7 +3509,7 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
sctx->runtime.has_occlusion_plane = false;
/* By convention we only snap to the original elements of a curve. */
if (has_hit && ob_eval->type != OB_CURVES_LEGACY) {
if (has_hit && ob_ray->type != OB_CURVES_LEGACY) {
/* Compute the new clip_pane but do not add it yet. */
float new_clipplane[4];
BLI_ASSERT_UNIT_V3(no);
@@ -3517,8 +3523,9 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
new_clipplane[3] += 0.01f;
/* Try to snap only to the polygon. */
elem_test = snap_mesh_polygon(sctx, params, ob_eval, obmat, &dist_px_tmp, loc, no, &index);
if (elem_test) {
elem_test = snap_mesh_polygon(
sctx, params, ob_ray, obmat_ray, &dist_px_tmp, loc, no, &index);
if (elem_test && !use_retopo_mode) {
elem = elem_test;
}
@@ -3565,6 +3572,31 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
}
}
/* Note: if both face raycast and face nearest are enabled, first find result of nearest, then
* override with raycast. */
if ((snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST) && !has_hit) {
has_hit = nearestWorldObjects(
sctx, params, init_co, prev_co, loc, no, &index, &ob_eval, obmat);
if (has_hit) {
retval = SCE_SNAP_MODE_FACE_NEAREST;
copy_v3_v3(r_loc, loc);
if (r_no) {
copy_v3_v3(r_no, no);
}
if (r_ob) {
*r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
}
if (r_index) {
*r_index = index;
}
}
}
return retval;
}

View File

@@ -2095,6 +2095,7 @@ typedef enum eSnapFlag {
SCE_SNAP_TO_INCLUDE_EDITED = (1 << 8),
SCE_SNAP_TO_INCLUDE_NONEDITED = (1 << 9),
SCE_SNAP_TO_ONLY_SELECTABLE = (1 << 10),
SCE_SNAP_RETOPOLOGY_MODE = (1 << 11),
} eSnapFlag;
/* Due to dependency conflicts with Cycles, header cannot directly include `BLI_utildefines.h`. */
/* TODO: move this macro to a more general place. */
@@ -2119,6 +2120,7 @@ typedef enum eSnapTargetSelect {
SCE_SNAP_TARGET_NOT_EDITED = (1 << 2),
SCE_SNAP_TARGET_ONLY_SELECTABLE = (1 << 3),
SCE_SNAP_TARGET_NOT_NONEDITED = (1 << 4),
SCE_SNAP_TARGET_RETOPOLOGY_MODE = (1 << 5),
} eSnapTargetSelect;
/** #ToolSettings.snap_mode */

View File

@@ -151,6 +151,16 @@ const EnumPropertyItem rna_enum_snap_element_items[] = {
"Snap to increments of grid"},
{SCE_SNAP_MODE_VERTEX, "VERTEX", ICON_SNAP_VERTEX, "Vertex", "Snap to vertices"},
{SCE_SNAP_MODE_EDGE, "EDGE", ICON_SNAP_EDGE, "Edge", "Snap to edges"},
{SCE_SNAP_MODE_EDGE_MIDPOINT,
"EDGE_MIDPOINT",
ICON_SNAP_MIDPOINT,
"Edge Center",
"Snap to the middle of edges"},
{SCE_SNAP_MODE_EDGE_PERPENDICULAR,
"EDGE_PERPENDICULAR",
ICON_SNAP_PERPENDICULAR,
"Edge Perpendicular",
"Snap to the nearest point on an edge"},
{SCE_SNAP_MODE_FACE_RAYCAST,
"FACE", /* TODO(@gfxcoder): replace with "FACE_RAYCAST" as "FACE" is not descriptive. */
ICON_SNAP_FACE,
@@ -162,16 +172,6 @@ const EnumPropertyItem rna_enum_snap_element_items[] = {
"Face Nearest",
"Snap to nearest point on faces"},
{SCE_SNAP_MODE_VOLUME, "VOLUME", ICON_SNAP_VOLUME, "Volume", "Snap to volume"},
{SCE_SNAP_MODE_EDGE_MIDPOINT,
"EDGE_MIDPOINT",
ICON_SNAP_MIDPOINT,
"Edge Center",
"Snap to the middle of edges"},
{SCE_SNAP_MODE_EDGE_PERPENDICULAR,
"EDGE_PERPENDICULAR",
ICON_SNAP_PERPENDICULAR,
"Edge Perpendicular",
"Snap to the nearest point on an edge"},
{0, NULL, 0, NULL, NULL},
};
@@ -3313,10 +3313,10 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "snap_face_nearest_steps", PROP_INT, PROP_FACTOR);
RNA_def_property_int_sdna(prop, NULL, "snap_face_nearest_steps");
RNA_def_property_range(prop, 1, 100);
RNA_def_property_ui_text(
prop,
"Face Nearest Steps",
"Number of steps to break transformation into for face nearest snapping");
RNA_def_property_ui_text(prop,
"Face Nearest Steps",
"Increase precision and smooth large changes by dividing "
"transformation into smaller steps (Face Nearest Only)");
prop = RNA_def_property(srna, "use_snap_to_same_target", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_KEEP_ON_SAME_OBJECT);
@@ -3379,13 +3379,13 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_snap_self", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NOT_TO_ACTIVE);
RNA_def_property_ui_text(
prop, "Snap onto Active", "Snap onto itself only if enabled (Edit Mode Only)");
prop, "Snap onto Active", "Snap onto active edited object (Edit Mode Only)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "use_snap_edit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_INCLUDE_EDITED);
RNA_def_property_ui_text(
prop, "Snap onto Edited", "Snap onto non-active objects in Edit Mode (Edit Mode Only)");
prop, "Snap onto Edited", "Snap onto non-active edited objects (Edit Mode Only)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "use_snap_nonedit", PROP_BOOLEAN, PROP_NONE);
@@ -3394,12 +3394,18 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop, "Snap onto Non-edited", "Snap onto objects not in Edit Mode (Edit Mode Only)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "use_snap_selectable", PROP_BOOLEAN, PROP_NONE);
prop = RNA_def_property(srna, "use_snap_selectable_only", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_TO_ONLY_SELECTABLE);
RNA_def_property_ui_text(
prop, "Snap onto Selectable Only", "Snap only onto objects that are selectable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "use_snap_retopology_mode", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_RETOPOLOGY_MODE);
RNA_def_property_ui_text(
prop, "Retopology Snapping Mode", "Optimize snapping options for retopology work");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
prop = RNA_def_property(srna, "use_snap_translate", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, NULL, "snap_transform_mode_flag", SCE_SNAP_TRANSFORM_MODE_TRANSLATE);

View File

@@ -714,6 +714,11 @@ void RNA_api_window(StructRNA *srna)
FunctionRNA *func;
PropertyRNA *parm;
func = RNA_def_function(srna, "is_operator_modal", "WM_active_modal_operator_test");
RNA_def_function_ui_description(func, "Test if an operator is actively running modal.");
parm = RNA_def_boolean(func, "result", 0, "", "Operator running modal");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "cursor_warp", "WM_cursor_warp");
parm = RNA_def_int(func, "x", 0, INT_MIN, INT_MAX, "", "", INT_MIN, INT_MAX);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
@@ -721,6 +726,13 @@ void RNA_api_window(StructRNA *srna)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_function_ui_description(func, "Set the cursor position");
func = RNA_def_function(srna, "cursor_warp_relative", "WM_cursor_warp_relative");
parm = RNA_def_int(func, "x", 0, INT_MIN, INT_MAX, "", "Offset of x", INT_MIN, INT_MAX);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_int(func, "y", 0, INT_MIN, INT_MAX, "", "Offset of y", INT_MIN, INT_MAX);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_function_ui_description(func, "Offset the cursor position");
func = RNA_def_function(srna, "cursor_set", "WM_cursor_set");
parm = RNA_def_property(func, "cursor", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(parm, rna_enum_window_cursor_items);

View File

@@ -310,10 +310,20 @@ void WM_paint_cursor_remove_by_type(struct wmWindowManager *wm,
void (*free)(void *));
void WM_paint_cursor_tag_redraw(struct wmWindow *win, struct ARegion *region);
/**
* Determines whether an operator is actively running modal.
*/
bool WM_active_modal_operator_test(struct wmWindow *win);
/**
* This function requires access to the GHOST_SystemHandle (g_system).
*/
void WM_cursor_warp(struct wmWindow *win, int x, int y);
void WM_cursor_warp_relative(struct wmWindow *win, int x, int y);
/**
* Set x, y to values we can actually position the cursor to.
*/
void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y);
/* Handlers. */

View File

@@ -278,14 +278,6 @@ void WM_cursor_grab_disable(wmWindow *win, const int mouse_ungrab_xy[2])
}
}
static void wm_cursor_warp_relative(wmWindow *win, int x, int y)
{
/* NOTE: don't use wmEvent coords because of continuous grab T36409. */
int cx, cy;
wm_cursor_position_get(win, &cx, &cy);
WM_cursor_warp(win, cx + x, cy + y);
}
bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event)
{
/* TODO: give it a modal keymap? Hard coded for now */
@@ -295,19 +287,19 @@ bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event)
float fac = GHOST_GetNativePixelSize(win->ghostwin);
if (event->type == EVT_UPARROWKEY) {
wm_cursor_warp_relative(win, 0, fac);
WM_cursor_warp_relative(win, 0, fac);
return 1;
}
if (event->type == EVT_DOWNARROWKEY) {
wm_cursor_warp_relative(win, 0, -fac);
WM_cursor_warp_relative(win, 0, -fac);
return 1;
}
if (event->type == EVT_LEFTARROWKEY) {
wm_cursor_warp_relative(win, -fac, 0);
WM_cursor_warp_relative(win, -fac, 0);
return 1;
}
if (event->type == EVT_RIGHTARROWKEY) {
wm_cursor_warp_relative(win, fac, 0);
WM_cursor_warp_relative(win, fac, 0);
return 1;
}
}

View File

@@ -2000,6 +2000,22 @@ void WM_init_native_pixels(bool do_it)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operator API
* \{ */
bool WM_active_modal_operator_test(struct wmWindow *win)
{
LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
if (handler_base->type == WM_HANDLER_TYPE_OP) {
return true;
}
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Cursor API
* \{ */
@@ -2038,6 +2054,25 @@ void WM_cursor_warp(wmWindow *win, int x, int y)
}
}
void WM_cursor_warp_relative(wmWindow *win, int x, int y)
{
if (win && win->ghostwin) {
/* NOTE: don't use wmEvent coords because of continuous grab T36409. */
int cx, cy;
wm_cursor_position_get(win, &cx, &cy);
WM_cursor_warp(win, cx + x, cy + y);
}
}
void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y)
{
float f = GHOST_GetNativePixelSize(win->ghostwin);
if (f != 1.0f) {
*x = (int)(*x / f) * f;
*y = (int)(*y / f) * f;
}
}
/** \} */
/* -------------------------------------------------------------------- */