Sculpt: Add Transform, Trim, and Mesh Filter operators to Sculpt menu #104718
No reviewers
Labels
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
5 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: blender/blender#104718
Loading…
Reference in New Issue
No description provided.
Delete Branch "Tarek-Yasser/blender:sculpt_mode_add_menu_operators"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Hello, this is a small PR to check that my understanding of #102427 is correct before moving on to the rest of the issue.
This PR contains the updated UI of the
Sculpt
menu only. Other menus will be submitted for review later.Currently exposed operators:
Scale (Could be left out?)The original issue specifies
Relax Face Set Boundaries
andErase Displacement
. I'm not quite sure if this is done in the UI code or somewhere else.Some notes:
'Toggle Visibility' and 'Hide Active Face Set' still need the
OPTYPE_DEPENDS_ON_CURSOR
flag to be useable.'Lasso Add' needs to be set to
Join
. Right now it usesDifference
by default.The mesh filters work surprisingly well! But they need some additions in the UI. These menu operators should be treated like other modal operators and need visual indications such as:
A visual change in the cursor to indicate that left-right movement is important
This is the same mouse cursor used in the Breakdowner operators
Keymap info in the status bar at the bottom to indicate to use
Esc
/RC
to exit andEnter
/LC
to confirmIf this is too much for now then these can be left out for now.
Otherwise I can see if one of the devs can give a helping hand.
There are also issues where the filter is only partially applied to the mesh, and clicking does not confirm the action.
I can not consistently reproduce this.
But anyway, those operations don't seem to work that well out of the box.
Sorry, I forgot about that.
Looking atsculpt_face_set.cc
, I assume I have to modify the flags insculpt_face_sets_change_visibility_exec
so that this works on 'Toggle Visibility' and 'Hide Active Face Set' only. Right?Update: tried it, only adding to
SCULPT_OT_face_sets_change_visibility
seems to make things work logically, i.e. You choose the option, you get a cursor, you click the face set you want to apply on.This is weird, I just looked at the last commit and it clearly shows
trim_mode = 'JOIN'
. I'll look if there's something I forgot to set.Update: Regarding this, I just tried playing with it for a bit. When building using
make lite
, it seems likeDIFFERENCE
acts asJOIN
for some reason (performance reasons I assume).With
make developer
things work as expected. ButLasso Add/Trim
seem to use the last trim mode set by the tool bar.Steps to reproduce:
DIFFERENCE
. Order doesn't matter.Not quite sure if I'm missing something or this is an actual bug...
I'll try looking for how to do this on my own for a bit. If I can't find anything, I'll ask on blender.chat.
Is there anything I can do on the Python side? I'm not sure if this is an issue with the settings I set or the implementation itself.
So I got the cursor icon displaying and got some text in the status bar. (With the help of PhilippOeser on blender chat).
To get cursor icons in the status bar it seems that I have to call
WM_modalkeymap_operator_items_to_string_buf
or its macro siblingWM_MODALKEY(_id)
._id
is usually an enum member (Look forKNF_MODAL_CONFIRM
for an example), which is supposedly hooked up to some keymap somewhere. I tried imitatingknifetool_modal_keymap
by doing the following:spacetypes.c
, I callED_keymap_sculpt
insideED_spacetypes_keymap
.ED_sculpt.h
, I added a new function declaration calledED_keymap_sculpt
sculpt_ops.cc
, I added the body ofED_keymap_sculpt
which callsfilter_mesh_modal_keymap
.sculpt_intern.h
, I added a declaration forfilter_mesh_modal_keymap
.sculpt_filter_mesh.cc
:FILTER_MESH_MODAL_CANCEL
andFILTER_MESH_MODAL_CONFIRM
as its elements.filter_mesh_modal_keymap
which does the same thing asknifetool_modal_keymap
but with the enum members.sculpt_mesh_filter_modal
, I callBLI_snprintf
similarly to howknife_update_header
.I've changed my changes to a branch
filter_mesh_modalkeymap
in case I'm missing some simple things.Minor thing: while looking at the status bar thingy, I noticed that
WM_MODALKEY
is defined in three places with the exact same definition. Perhaps we could pull this out?Currently, the modal doesn't have cancelling, not sure if I should dive into that or if this is something for another issue? I'd be more than happy to do it. I did try to follow the logic and read The transformation engine's principles page, but I quite quickly lost track of what I'm supposed to be reverting and where its stored.
As for the mesh filter applying only to part of the mesh, I'll try playing around with the filters to figure out what's causing this.
The
SCULPT_OT_face_sets_change_visibility
menu operators work perfect.And yes the Trim/Add operators behave very strange. The lasso entries don't have
Join
orDifference
set so it's hard to add them to quick favorites or as shortcuts.I can also confirm that box trim/add sometimes gets confused and does the opposite.
For the status bar and modal keymap, can you look into the transform operators like Move for reference? They directly use the modal keymap entries for the status bar.
Using
RC
andEsc
for canceling is definitely important.OPTYPE_DEPENDS_ON_CURSOR
should also be used for the mesh filter menu operators as well actually. They also use auto-masking, so the cursor position is important to determine which mesh island/face set is used.I suggest that if the mesh filters take a lot more time and effort, that you can also split the PR into two, so the others can be merged as soon as they are ready.
Let's see if @JosephEagar can give some help. Otherwise keep asking in blender-coders like before.
Can't quite figure out what you mean by "don't have 'Join' or 'Difference' set". Can you please elaborate?
Hmm, I didn't quite remember if I tried to break Box Trim/Add. Would this weird behavior warrant a bug report/issue? Would be quite helpful to first know if this is something related to the code I added (mostly UI stuff) or the core operator though.
I'll try diving back into this rabbit hole soon.
Seems like it's not quite trivial to revert changes for mesh filters. I recall being blocked by some parallel task thingy that made me lose track of where's the data. Hopefully once I check it out with a clear mind I'll be able to figure things out.
Will do!
Well, most of the time is me roaming around in the code or documentation trying to make sense of what goes where. I'll give the mesh filters one more session before splitting them out if I can't get them done.
I've not done a lot with modal tools. The last one was actually the original knife tool 10 years ago.
I cleaned up the mesh filter code a bit. I rebased your patch; unfortunately it seems you can't make pull requests of personal repo forks, but here's the branch: https://projects.blender.org/JosephEagar/blender/src/branch/rebase-104718
Don't forget about OPTYPE_DEPENDS_ON_CURSOR. It should fix mesh filter.
I might've gotten a bit sidetracked :)
So after spending a few hours yesterday, I (think) I got the statusbar almost working?
The current state is that the modal keymap is defined on the python side and hooked up on the C side.
The current status bar for the mesh filter works fine, but for some reason it shows text instead of the icons.
I asked around a bit on
blender-coders
, but no one seemed to have an explanation.Also,
Preferences/Keymap
now contains an entry forMesh Filter Modal Map
.The next few sections detail how I implemented the modal keymap, using it in the status bar, setting the mouse cursor, and some bugs? I encountered. I mainly documented these because I couldn't really find this information by searching the dev wiki or the code. Hopefully they're good enough to help others.
For the time being, I'll be moving on to the rest of the original issue unless there's something I can do. I'd also appreciate some feedback if possible.
Adding a modal keymap
expand me for details
Keymaps are used to associate input events (mouse/keyboard/etc) with values in the code that can be used to determine which action was taken.
For example, most operators support
Esc/RMB
to cancel them. Keymaps help tell Blender that for a certain operatorEsc
andRMB
both map to the actionMY_OP_CANCEL
.The code for the operator can then properly handle all actions without having to handle
Esc
orRMB
explicitly. This also gives us the ability to remapMY_OP_CANCEL
to whatver key if we so wish without having to modify the operator's code.The operator only cares about the actions it defined, the mapping between buttons and actions is defined somewhere else.
Here's a high level overview of what you need to add a keymap:
Go to
release/scripts/presets/keyconfig/keymap_data/blender_default.py
.Scroll down to a similar keymap to the one you want to use, for example
km_sculpt_mesh_filter_modal_map
. It should look like this:The most important things here are the keymap's name
Mesh Filter Modal Map
and theitems.extend()
call.Try to keep the keymap name consistent in the Python and C code so it's easy to reach either side.
items.extend()
takes the actions you want and registers them as actions for the keymap specified above.Don't forget to call your function in
generate_keymaps_impl
!For your keymaps to show in
Preferences/Keymap
, you need to add it tokeymap_hierarchy.py
Go to the operator you want to add a keymap for. You have a function similar to this. If it doesn't exist, you need to implement it.
Notice how "CONFIRM" and "CANCEL" on the Python and C side seem to link an action to its keys.
For example:
FILTER_MESH_MODAL_CONFIRM
action, which maps to a "CONFIRM" action.So whenever you left click while using the modal filter mesh, whatever code handles
FILTER_MESH_MODAL_CONFIRM
will run.Note that you'll have to define an enum for the actions so you can handle them in C code later on:
Add the modal keymap function to the appropriate header file.
For
filter_mesh_modal_keymap
, it can be added insculpt_intern.hh
, just under the declaration for its operator function:The C modal map function doesn't get magically called, you need to call it somewhere.
For example
filter_mesh_modal_keymap
gets called inED_keymap_sculpt
.Similar functions are usually in a file called
x_ops.cc
. For sculpting operators, you can find this function insculpt_ops.cc
. If it doesn't exist, you can create it as follows:You'll probably have to add the function's declartion in a header file somehere. For the previous function, you can add its declaration in
ED_sculpt.h
ED_keymap_sculpt
doesn't also get magically called, it's itself called inED_spacetypes_keymap
Once you have your
op_modal_keymap
orop_keymap
, you should be able to see your keymap inPreferences/Keymap
and use it in status bar messages!Needs further clarification:
What does
"space_type" : 'EMPTY'
mean here? What about "region_type"? I couldn't find much information about what this dict's fields mean and what their options are.None
for?FILTER_MESH_MODAL_CANCEL
is the action that might mapped to multiple keys"CANCEL"
is some string that's shared between Python and C0
for?""
What's this for?How to use modal keymaps in status bar updates?
Here's also some info I gathered about how to use a keymap for statusbar upates:
expand me for details
For modal keymaps, you just need to call a function like this one inside your modal callback.Don't forget to clear the statusbar by calling
ED_workspace_status_text(C, NULL)
once your operator is done.Changing the mouse cursor for modals
Just call
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EW_SCROLL)
for example in your modal callback.Don't forget to call
WM_cursor_modal_restore(CTX_wm_window(C))
once the operator finishes!You can find the different types of cursors you can use in
wm_cursors.h
Cancelling mesh filters
Regarding cancelling mesh filters, I spent a fair bit of time trying to understand how the transform tool cancells its operators.
Here's what I understood:
restoreTransObjects
(transform.c
lines 925-937 iirc)transformEvent
.restoreTransObjects
copies that data back.transform_cancel
) doesn't do anything special? I think it mostly does cleanups?So, to apply this to mesh filters, one would have to:
I'm not 100% sure that this is completely applicable to mesh filters yet, but it might be a good starting point.
At this point, I think the cancel functionality should be split into its own issue, unless a more experienced developer deems it easy enough to be bundled into this issue.
Bugs?
After using a mesh filter, upon trying to use it again this assert fails:
I'm not quite sure what's the cause of this. I read the usage guide at the top of
sculpt_undo.cc
and confirmed thatSCULPT_undo_push_begin
is only called ininvoke
andSCULPT_undo_push_end
is only called once at the end of the operator (confrimation or cancellation)I did try to only use in when the operator is confirmed or cancelled only in case I understood the usage guide wrong, but it seems to crash still.
There seems to be a memory leak related to cancelling mesh filter operators:
What's weird here is that this only occurs when the operator is cancelled. Confirming the operator doesn't seem to leak memory.
What's even weirder is that when confirming, no deallocation logic is specific to confirming or cancelling, both do the same cleanup logic.
What's the most weird is that the only following filter types have specific initializations that allocate memory. The others don't:
Here are the steps to reproduce:
I implemented a cancel function for mesh filter in master, sculpt_mesh_filter_cancel (it's not used yet since mesh filter's modal map currently lacks cancel). Note that you still have to call sculpt_mesh_filter_end.
Okay that "close" button looks awfully like a "cancel" button for comments.
Thanks a bunch! I'll integrate that as soon as possible.
I implemented a modal keymap for linking nodes a few weeks ago, see
dc79281223
Thanks for the suggestion! I checked out your commit and I think I implemented something similar in
bfa51d9bd6
. I think the main issue here is that there's no documentation for this, or it's not quite acessible. Perhaps the big comment I posted a few days ago can be added to the dev wiki or something (after being iterated upon, of course.)?@JulienKaspar
I think the only thing left for this PR is the aforementioned bugs:
Sphere
Erase Displacement
if no Multires (Can't quite understand what'sMultires
, but hopefully I can pick up on it once I look around).poll()
.@Tarek-Yasser Good to hear!
Sorry for being unclear on
Erase Displacement
. It's used when the object has a Multiresolution modiifer with subdivision levels.So in the case that no Multires modifier is used for sculpt mode on the active object, the operator could just as well be grayed out, since it's not doing anything.
Just spent a few hours looking into this. I toyed a bit with
poll()
and it indeed does disable the operator's entry in the menu. The main issue I'm facing is knowing which entry I'm in.For example, when I use a static variable to alternate enabling and disabling entries, I get this pattern:
Using this snippet, I was able to enable or disable the hole Filter Mesh section when a multires modifier was added with a sculpt subdiv > 0:
Here's how it affects the menu:
I'm pretty much out of ideas now. The other operators seem to not be grouped by like Filter Mesh is so I'm not sure if there's a way to do this without splitting the operator up.
Also, I'd like confirmation whether or not checking
sculptlvl
only is correct, there are other "lvl"s but I'm not sure if this menu cares about them.@JosephEagar thoughts?
Ok, I think I'm done with this PR, It's getting a teeny tiny too big.
I've spent the last couple of days investigating the memory leak and the greying out of Erase Displacement to no avail so I suggest that the remaining stuff may be spun into their own issues. I'll continue working on the main issue.
Here are the things that I think deserve an issue that are related to this PR:
Possbily a bug in the implementation? The Python code exposing the operator in the UI just sets the operation type so I'm not sure why it's bugged.
Not easily replicable, but might be worth an issue just to figure out how to reproduce it.
Tried compiling with the current main branch and the parent branch of this PR's branch, both don't seem to have this issue which points me towards the operator cancelling code. compiled a dev debug build and got some address sanitizer output attached below. Hope it helps.
Checking in
poll()
works fine, but I didn't find a way to check which operator we're polling for given only the context. I'll revert the code but my trial can be found at this commit115ec325f2
@JulienKaspar @JosephEagar Please review and tell me if there's anything else I can do.
The
nodes
out parameters fromBKE_pbvh_search_gather
needs to be freed,MEM_SAFE_FREE(nodes)
, that was probably my fault. Not sure what you mean by partial application of mesh filters, I'll take a look.Let's ignore multires for now; I need to figure out how to best expose the sculpt data backing type to python (multires is the PBVH_GRIDS type).
The CONFIRM modal item needs a RELEASE entry for LEFTMOUSE.
I've marked a few nitpicks (C++ files should use
nullptr
instead ofNULL
, and//
comments are not allowed).@JosephEagar
The notes you mentioned should (hopefully) be addressed now.
For the memory leak, I did try freeing
nodes
, but the unfreed blocks message still showed up.I inspected the address sanitizer output a bit more and noticed that the biggest chunks that leaked were related to the undo stack. I did some debugging and got the idea to check where we push/pop and noticed that
SCULPT_undo_push_end
usedSCULPT_undo_push_end_ex
withuse_nested_undo
set to false. Changing it to true seems to have fixed the leak. I did my usual memory leak test by runningRandomize
and cancelling it, and no leak was reported! I also tried a couple of other Filter Mesh options and they also didn't leak.Speaking of the undo system, this change introduced a small bug related to undo that I explained and fixed in this commit
WIP: #102427 Sculpt Mode Add exisiting Tools as Menu Operatorsto WIP: Sculpt:Add exisiting tools as menu operatorsWIP: Sculpt:Add exisiting tools as menu operatorsto WIP: Sculpt: Add exisiting tools as menu operatorsWIP: Sculpt: Add exisiting tools as menu operatorsto Sculpt: Add Transform, Trim, and Mesh Filter operators to Sculpt menu@ -704,0 +717,4 @@
static const EnumPropertyItem modal_items[] = {
{FILTER_MESH_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
{FILTER_MESH_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
{0, NULL, 0, NULL, NULL},
NULL -> nullptr
@ -860,2 +873,3 @@
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_X | OPTYPE_BLOCKING;
Add OPTYPE_DEPENDS_ON_CURSOR too.
@ -1084,3 +1172,3 @@
ot->ui = sculpt_mesh_ui_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
// Doesn't seem to actually be called?
Don't use // comments.
Hi
Lasso trim and lasso add are the same, both are trimming, are the same operator and mode.
I didn't know if I should put it here or sould I open an issue.
Hello, I think I already reported this in an earlier comment, also #105557 seems to mention this. Sorry for the inconvinience and hopefully it'll be fixed soon.
I'm unavailable for this month, but I'll see if I can review this towards the end of the week.
Ah I see this was already committed :/
I tested it a bit more in main. There are still a couple of bugs that I noticed but overall it works great!
Thanks @Tarek-Yasser @JosephEagar for your work!