This task is to outline how multi-object mode support might be achieved.
This design task focuses on mesh edit-mode, I think this is one of the more complicated cases,
so supporting this should lead us to tackle most of the more tricky problems.
Motivation
Maybe obvious, including this for completeness - in case I miss something.
Certain operations are useful to apply on multiple objects at once.
Examples are:
- Transform
Ability to move vertices from many objects at one for eg. Change positions in relation to each other, snapping multiple vertices from different meshes to one location.
This avoids the hassles of enter/exit modes for otherwise simple tasks.
Note that IMHO this reason alone makes it worth adding support for this, even if not all tools have support for it.
- Basic Tools
Calculate normals, triangulate faces, cleanup operations - it's useful to support this without users having to write batch processing scripts that iterate over selected objects.
- UV unwrapping.
It's common for objects to share a texture, especially for real-time models.
Note that this alone is not a compelling reason to add multi-mode support IMHO, we could support this with an operation in object mode, it would be clumsy but functional.
Design Challenges
- How to handle tools which join data? - select 2x vertices and making an edge for eg.
I think attempting to make this work at all is likely to cause more confusion than it's worth (move geometry between meshes, or create the missing geometry to the last selected vertices object).
Suggest to warn that geometry from different meshes can't be joined.
- Active Object
Currently the editmode object is always the active object.
Is this something we would expose (draw differently for eg).
Since we will have UV/vgroup/shapes from the active object in the UI we can't avoid the fact that the user still needs to be aware of the active object.
Should we make it that selecting a vertex from another mesh makes this become the active object (think this would be reasonable, just noting it in case there is a down-side).
- How to handle linked duplicates?
Two or more objects can share the same data, in some cases we could operate on both (border select for eg), other times - transform for eg. We probably have to pick one and not use the others.
- How to handle data layers? (UV's, vertex groups, shape-keys).
AFAICS there are no elegant solutions here. If someone changes a uv/vgroup... etc, all we can do is change all other objects to the same name or index.
- Accidental/Slow Mode Switching
Even if this is working well, there is the possibility of the user accidentally entering edit-mode on 100's of objects.
(A little like the problem we used to have pressing P by accident entering the BGE).
Since changing modes for many objects could be slow, I think the best we can do here is to check for Escape key being pressed after each object is converted. That should at least avoid a user locking up their Blender instance by accident.
Another option is to have a popup asking the user if they _really_ want to enter editmode on a huge number of objects, but think this is weak.
- Selecting a single object
With multi-object editing users may want to select a single mesh.
not essential, but users will likely want this.
- Toggling individual objects
Users will likely want to add/remove objects from the current edit-mode.
more of a nice-to-have, mentioning for completeness.
Technical Challenges
- Partial Rollback
When one operation fails, we will want need a reliable way to roll-back an edit for all other objects.
The undo stack is capable of this in principle, we just need a good general API than handles this.
Note that this isn't necessarily always the case, we can:- In some cases these checks can be performed before making any destructive edits.
- Print a message "Operations on ... objects failed", without rolling back changes, in some cases this is preferable as long as it's not leaving user data in a broken state.
- Faking 'One Big Mesh'
Some operations will need to *pretend* that all objects are one bug mesh (uv unwrapping for eg). This can be done on a case-by-case basis.
Examples of this are:- Transform
Don't think this will be too hard to support, although there will be some complications regarding local/global transform spaces. The way transform supports a matrix per transform element means we can support this. How to handle local-space operations is not well defined, We could use the active objects local matrix, or each objects local matrix - however I think this may give confusing results in most cases. - UV unwrap
Only requires extracting faces from all meshes. - Knife tool
This is much more complicated, it may be an exception where we don't support multi-object initially, There might be a trick to support it more easily (have an array of knife-mode data with is aligned to each object), but wouldn't count on this since we most likely want objects occluding eachother for eg. - Intersect
This is one of the few cases we might need to join the mesh then split it apart again after. That - or we don't support multi-object modes for this tool. Joining has the big disadvantage that it can loose precision and it's likely to cause errors with custom-data layers (some objects have uv's/vgroups/multires, some not).
- Transform
- Undo Stack
Getting this working usefully may be entire project on it's own, From checking on the code, seems we can group UndoElem into UndoElemGroup so many undo operations can be performed in a single step.
Note that the current code relies on undo running on the active object. (undo doesn't store pointers to the object). If we have objects which are not the active object being included in undo, we must have a fool-proof way to get the list of objects back.
This might not be so simple - relying on visibility for eg, depends on view layers, so calling undo from a different window will give a different set of objects.
Edit-mesh undo already stores data pointers in undo structure, so we might be able to use these instead of the contexts objects.
Need some investigation to ensure this is going to work reliably.
Another option is to force the same context when running undo, so it's not possible to execute undo in a different view layer (it could be done internally, the user need not be aware of it).
Initial Steps
This isn't complete, it's just some ideas on how to start out developing this in a branch.
- All selected objects in a matching mode are grouped (even if not active)
- Active object is an exception, it's included even when not selected.
This is just a simple rule to start with, we might up using a different rule. - At first we don't handle undo at all (since this might rely on larger refactor to the undo stack).
Task Breakdown
- Drawing Code
Use mode drawing for all selected objects with the appropriate data, type.
Currently the convention is to only show the active-object w/ mode drawing, this convention shouldn't be hard to break.
- Tools
Basic design for making it possible to operate on many objects at once.
Possible API's:
Object **ob_array = ED_object_mode_get_all_ex(window, scene, OB_MESH, OB_MODE_EDIT, &ob_array_len);
Object **ob_array = ED_object_mode_get_all(C, OB_MESH, OB_MODE_EDIT, &ob_array_len);
/* we could use an iterator too */
FOREACH_OBJECT_IN_MODE(view_layer, OB_MODE) {
BMEditMesh *em = ...;
}Have `*_single()` and `*_multi` versions of functions, to avoid confusion.