From BlenderWiki

Jump to: navigation, search
Note: This is an archived version of the Blender Developer Wiki. The current and active wiki is available on wiki.blender.org.

Shape key workflow enhancements — Design document

WIP
Everything is subject to change.


Grigory Revzin’s GSoC, mentored by Bastien Montagne (mont29)

My email: revzingg at gmail.com, IRC: revzin

Re-done version of this proposal here.

Synopsis

An update for the Shape Keys panel GUI for more productive and powerful relative shape key editing workflow to facilitate shape key-based facial rigging & under-the-hood changes to support the new GUI.

From the user’s POV

Overview

Current Blender Editmode Panel for shape keys is replaced with a new much more interesting one!

The new UI is an editing-blending-centered interactive shape list representing each shape key via a four-way slider. The user can select the active shape key with a radio selector.

The panel is meant to be available in Edit Mode for Mesh objects with shape keys.

Each four-way slider, when dragged, blends (adds or interps, based on the keyboard state) the represented shape key to the internal scratch shape key (rather than the active shape key). Vertical slider movement controls the proportional falloff for blending connected vertices. The user can perform editing on top of a shape key mix, either the mix defined by animation data or a temporary editing mix. All user transform edits are affecting the scratch shape key.

Shift-hovering over the given slider previews the corresponding shape key. MMB<ref>Or some other modifier key </ref>-dragging the slider sets the shape key value in the underlying mix.

Another concept is the Scratch shape key. Currently, all editing happens in-place (the KeyBlocks get updated immediately after the edit happens and when the key is switched). The idea is that all editing happens to an extra internal Scracth key that can be committed (or not) to the active key.

The internal scratch shape key is updated when user selects a new active shape key. The changes in scratch key are committed to the previous active shape key either on user’s demand or automatically if the Auto-commit changes checkbox is set. Auto-committing can be turned off in case the user wants to change the active shape key without committing the changes (useful if you just found you've edited the wrong shape key).

The user can blend the scratch key from itself and adjust the base mix intensity with special sliders under the shape key list. The Scratch shape key can be discarded, i. e. reset to the current active shape key.

The user can edit a given shape key in isolation or on top of a mix ('pin'). The mix can be either from existing animation (like 'Apply Shape Keys in Editmode') or a temporary one, non-dependent upon existing animation.

Blend from Shape/Four-way slider

A click and drag on the slider blends the corresponding shape key into the current one (the scratch). By default, it adds the relative offsets of the current key to the selected vertices, with CTRL-key it interps the selected vertices of the scratch key toward the "dragged" key. Vertical drag adjusts the falloff distance from selected vertices. As soon as the mouse button is released, blending stops.

I. e. we select the wanted vertices, and drag the corresponding shape's slider to blend it in, adjusting intensity and falloff on the fly.

When working with absolute keys, only interpreting is supported.

The Blend from Shape is invoked by dragging the four-way slider.

X mouse drag

adjusts the blend intensity. The left of the slider corresponds to the zero blend factor, the right of the slider corresponds to 1 blend factor.

Y mouse drag

adjusts the blend falloff distance. The lower slider edge corresponds to no falloff distance, the upper slider edge corresponds to a falloff distance proportional to the object bounding box diagonal.

Continious Grab

If Continious Grab is set, then ’overdrag’ can happen to increase falloff distance or to go to negative or larger blend values. Negative falloff distances won’t make sense in the proportional editing implementation, but maybe later :)

Would rather call it Overdrag (Continuous drag is already taken as general UI feature for all sliders). --Mont29 18:53, 26 May 2014 (UTC)

CTRL-dragging

the slider switches from Adding to Interpreting (if the shape keys mode is absolute, only interpreting is available).

Note Shift and Ctrl are already 'taken' for sliders (they respectively make dragging more precise, and with more “integer” steps). Would suggest using Alt here, but it would conflict with options like 'emulate MMB'. So perhaps directly do this: LMB-drag for 'add', MMB-drag for 'ctrl'? Not sure we want to pile so much in a poor slider though, even a 2D one! Might be better to just add as a toggle option… And it might not be that easy to pass that to the operator either. --Mont29 18:53, 26 May 2014 (UTC)

New operators

Blend from Shape
is upgraded to support connected vertices proportional blending and modal editing to support 2D four-way sliders.
Shape Propagate
is upgraded to support connected vertices proportional blending.
Update Scratch
updates the Scratch shapekey from the current active (Reset Scratch?)
Commit from Scratch
commits the changes.

Workflow notes

The proposed workflow (and internal processes) with the Blend Mode GUI is described in detail:

  1. User selects a shape key for edit (the active shape key) with the radio on the left from the slider.

  2. The shape contents are transparently copied to the Scratch.

  3. User makes edits, transforming the shape, sculpting etc. The edits are applied to the Scratch key internally, which is on-screen.

  4. User ends the shape editing with one of the following:

    1. User selects another shape key as the active and Auto-Commits are enabled.

    2. Exec of “Commit to Active”

    3. User exits to Object Mode

    These events commit the scratch shapekey contents to the active shapekey.

The editing happens on top of the mix set with Mix Mode (if Apply Shape Keys for Edit Mode is active).

While being in Blend Mode, the user can:

  1. Preview any shape key in isolation by Shift-hovering over the respective 2D slider. This allows to compare the currently edited shapekey to another one and to the previous version of itself easily.

  2. Blend (Add/Interp (with Ctrl-drag)) from any shape key, including the scratch itself and the base mix by horizontally dragging the respective 2D slider. The vertical drag influences the proportional falloff distance around the selected vertices, if any. Falloff types can be switched via mousewheel rotation with an icon drawn next to the mouse pointer or elsewhere in the 3D View.

    Initial PE falloff type can be obtained from the Context’s ToolSettings.

    If the Continious Grab checkbox is active, the blending can go over the slider’s limits.

  3. Edit the base mix values with RMB (or MMB?)-dragging the respective sliders.

Committing the shape changes

The user has an option of auto-committing the scratch key. Currently, the EditMesh shapes get into the RealMesh on

  1. Shape key switch
  2. Edit mode toggle
  3. Undo push

Auto-committing will work the same way if enabled. The Scratch's contents is comitted to the active key at the same points. If it's turned off, then the artist can commit the contents of the scratch to a different shape key.

Improvements summary

The following table summarizes the different benefits of the new Blend Mode UI.

User’s situation User’s current actions User’s actions in the proposed UI
User needs to blend in some vertices from another shapekey. Blend from Shape will leave distortions since connected vertices won’t be affected. Proportional falloff will kick in, the falloff distance can be easily adjusted with the four-way shape key slider.
User wants to compare the results of his edit to the pre-edit shape key. The user has either to spam Ctrl-Z and Shift-Ctrl-Z or to create a backup of the original shape key and switch back and forth. The user can temporarily view the original shape key by Shift-hovering the mouse over its slider.
User edits the wrong shape key (happened a lot to me at least). All user’s actions have an in-place effect, so he has to Ctrl-Z, losing either the original shape key or his edit. User (if he notices the mistake in time) can commit his edit to the another shape key by disabling automatic commit, thus preserving his original key and his edits.
User wants to compare the shape key he edits to some other shape key. This requires the user to select another shape key as active and go back and forth, which can be frustrating if the keys aren’t close in the list. Once again, he can Shift-hover above any shape key to compare to it.
User wants to perform Blend from Shape and adjust intensity and, in perspective, the falloff distance. He has to move the mouse all the way to the operator “popup” in the View3D’s tool panel. The mouse doesn’t need to leave the Shape Keys panel. Falloff distance is adjusted here as well.
User wants to Add/Interp shape keys to the active shape key. User has to manually select the shape key (from which to blend) from the Blend from Shape’s dropdown, once again moving the mouse to the View3D’s Tools and slowly finding the needed shape key in the dropdown, especially if there’s a lot of shape keys. No need to leave the Shape Keys panel, just drag sliders. Ctrl-drag switches the mode from default Add to Interp.
A Python coder wants to automate some shape key blending, to e. g. generate some facial expressions from mocap data. Either slow (from Python!) direct RNA data manipulation or using the clumsy operators. Important algorithms such as PE stuff is useable from the Blend from Shape operator. Sandbox key — the Scratch — is provided.
User wants to sculpt a shape key on the top of existing mix. If the shape key values are animated, user can’t set an arbitrary mix. The user can decide if he wants to use the a temporary mix or the mix defined by F-curves.

An extra unrelated improvement

One of the targets of the project is to reduce the .blend file size by storing only the changed vertices (from the rest position) per a keyblock in the file (uncompressing them in runtime). Should help quite a lot on shapekey-rich characters.

Implementation details

UI, updated slider list, and shape blending

Proportional editing connectivity distance calculation is a considerable bottleneck on big meshes. For example, in the D354 patch adjusting the falloff distance recalculates the vertex connectivity distances every time and the hangs are noticeable even on Elizabeth’s head (4K faces). Imagine the transform engine would recalc the falloff factors on every tick!

So the updated Blend from Shape operator should drop exec behavior for use from the UI. The proportional distances should be calculated only on op invoke. Modal handler should just recalculate the factors based on the available connectivity distances (pretty fast!).

A little problem here is that the operator will need to directly know the changing UI state while running modal which doesn’t happen a lot in Blender aa — but we can’t just call exec each time the slider has changed, this means distance recalc and this is slow! The modal handler will have to know the slider’s width, height and the mouse’s coordinates in the slider.

Proposed implementation:


UI implementation

UIList changes

We will need a way to define the item height from Python scripts, as a slider is going to be at least twice the height of a regular UIList.

This is going to be implemented as an optional parameter of a uiTemplateList, changing the height of the items per a uiTemplateList instance.

This won't be hard to do as the height is now controlled by a simple define.

The four-way slider

WIP
This is very, very WIP at the moment. Please refer to the user's PoV description to see what we want to have!


Requirements for the slider operation

  1. When the user clicks the slider, Blend from Shape should start working and alloc the resources for storing the connectivity distances.
  2. Blend from Shape should blend/add the shape key it corresponds to the active (scratch, internally) shape key. The x-position of the mouse in the slider should define the blend value, the y-position - the proportional falloff distance.
  3. While
  4. The BfS, while the slider is dragged, continiosly updates the blend value and the shape value. It should not recalcualte the connectivity distances! This is slow and pointless as they don't change!
  5. As users releases the slider, BfS should release resources.

Requirements for the slider's implementation

  1. Must make the slider drawable from a Python UI call
  2. The slider must draw a proportional edit circle in View3D when dragged. ?: what if there're more than one view3D? Where to get the projection to projected mode PE? --- From the last View3D that was touched. Need to make a way to determine this.
  1. They all are! slider would be yet another Widget (Button) type, so...
  2. Hrm… Slider won’t draw anything, that’s for sure! Only one that could draw something would be the modal operator, but since it does not run in a 3DView, I would rather suggest to hack the 'preview weights' feature, so that areas of influence are shown in red, and areas untouched, in blue.
--Mont29 19:04, 26 May 2014 (UTC)

Slider implementation ideas

Four-way sliders are housed within an updated UIList.

A helper function is to be provided that blends the shapes:

void blend_keys(KeyBlock *from, BMEditMesh *to, int interp, float val, float *distances, float *factors);

The slider 'class' is to have two RNA properties to control one for x-dragging and other for y-dragging. It is to behave exactly as the current sliders do, but control two props. Can add a UIItemPair UI layout function for drawing these.

However, if Blend from Shape is passed as an argument to the operator, it has special logic:

  1. Slider clicked for the first time
    1. Allocate scape for falloff distances and factors
    2. Compute falloff distances from the current mesh state
  2. While the slider's being dragged:
    1. Continously call blend_keys
    2. Schedule a redraw
  3. When the slider's released:
    1. Release the distances and factors
    2. Schedule one final redraw
  1. Add MESH_UL_shape_keys_blendsliders, where draw_item draws the 2D slider with related to the key - that should be enough for the slider.

We can make the slider customizable by supplying some to it: what operator to call and what properties to set. If none are (or some magic string is passed), it can default to its shape blending behavior.

Several issues here:
  1. First of all, we do not want a 'blend shape key' slider, we want a generic 2D slider.
  2. That part of the project is not the easiest one, by far. Adding a second RNA pointer to Button is not a problem, but linking it to the modal operator won’t be simple. My best idea currently (but I’m not an op expert) would be to place those two RNA values in shapekeys themselves, unless there is a way for a button to start a modal op and then 'attach' somehow to its parameters… All this sounds rather hacky to me. :/
Another Option would be to run the op in the slider callback, but this would mean non modal execution, not necessarily an issue provided we cache the connections data?
--Mont29 19:18, 26 May 2014 (UTC)

The slider list

Implementation notes

  1. We will be updating UIList to support arbitiary fixed-height items per instance. Most UILists will end up having the same height, ofc, but we'll need this one about 2,25 UI units.

Editing the shape keys

Attempt at shape key recalculation speedup

In the master branch, switching between different shapekeys in editmode is internally re-entering editmode with a different shape key loaded. Offset propagation is done by a heavy-duty function that converts a BMesh to Mesh. While this is a very safe method that accounts for topology and customdata changes, it's slow due to memory reallocations.

It is uncommon to edit topology while sculpting shape keys. It happens, of course, but not often, so it's rather pointless to run heavy-duty conversion functions each time (and reallocate everything) since

  1. Most shape keys don't change in a deform-only edit, just the current one. Usually the number of basis shape keys is very small compared to the total number of keys.
  2. If the mesh topology can be assumed to be unchanged, we can avoid rebuilding the whole RealMesh, but only edit the affected shape keys.

To help determine if the topology has been changed, a hash of the BMesh topology-related data is computed (see this commit. This is a cheat somewhat, as it doesn't check the topology itself, but the memory layout, so a copy of the current editmesh will be treated as a different mesh... but that doesn't seem to be a problem, editmesh reallocation doesn't happen much without real topology changes.

Attempt at undo speedup

Current undo implementation saves the whole mesh (converted from the EditMesh) with all the shape key blocks, even unchanged ones. This creates a great overhead and is slow.

The idea is, at least, to store only the changed keys (if the topology didn't change). This is going to make a lot of difference in terms of memory usage (a relatively hi-poly character with lots of shapes is measured in dozens of megabytes per one undo level).

Editmode changes

With all above noted, we change some shapekey-related code in editmode.

Shapekey handling

  1. On Editmesh creation
    1. Create the BMesh from the Mesh with a bmesh_mesh_conv routine.
    2. Put it to editmesh's coordinates.
    3. Compute and store the editmesh topology hash
    4. Set the scratch key to track the active shape key, copy over the keyblock data to it.
    5. Evaluate the shapekey mix, if there is any, and copy the result to Bmesh coords. Otherwise, just copy the active key's coordinates there.
  1. On Active shape key switch
    1. Check if Auto-commit
    2. Check if topology has been changed
    3. If the topology has been changed, run the usual 'heavy' conversion functions and end.
    4. If the topology hasn't been changed,
      1. Push the current editmesh coordinates to the scratch shape key
      2. Determine if the 'old' keyblock referenced by scratch->origin is a basis for any other shapes
      3. If so, determine the offsets (*) and push them for each dependent key directly in the keyblock
      4. Update the 'old' keyblock from the scratch
      5. Update the shapes stored in bmesh customdata
      6. Re-evaluate the shape key mix
  1. On Editmesh load to Realmesh the process is the same as in shapekey switching, only we don't do re-evaluation of shape keys.

(*) if there is a mix currently, the editmode code emulates the modifier's behavior by subtracting all mixed keys except the active one from the editmesh coordinates, scales the active key back to 1.0 weight and that value is used to commit. This means if we edit the key at 0.5 value, we still get the keyblock data edited as if it was edited 'pinned'. From the user's PoV, it means he'll get exactly what he sees in editmode: if a keyblock was edited at 0.5 value, the 'real' edit will be 2 times larger and the user will see it multiplied by 0.5.

Figure: current state of editmode shape keys panel. Note how Apply Shape Keys in Edit Mode is gone, leaving us with pinning/unpinning only, and a possibility to use an alternative mix source in editmode if the main mix is animated...

Mix and Pinning

A decision has been made to not use the shape 'modifier' code to get mixing in editmesh. Edit cage deforms result in wrong manipulator handle coordinates depending on the mix parameters and generally won't be easy to optimize for new editing routines. Moreover, it creates a slightly weird duality in Edit Mode, where Pinning does pin the key in object mode, but doesn't in editmode if Apply Shape Keys in editmode is set. One of the already-accomplished goals was to get rid of the Apply Shape Keys in Editmode and leave the user with only Pinning available (*). The user can switch from Pinned to Unpinned any time to view the shape in mix or in isolation.

(*) for relative shape keys. For absolute shape keys, it will take a good time to guess what is the correct way to interpret in-mix editing so absolute shape keys will be always pinned in editmode.

An alternative mix source

Another problem in master Blender is that the user can't use an arbitiary shape mix to sculpt on if the shapes are animated in object mode. The proposed workaround is adding an alternative mix value that the user can switch to when doing editmode work. The user can switch from/to the temporary mix value with a Key's RNA property (see this commit and the picture above).

These changes has been mostly implemented in the gsoc branch during the last few weeks. It's work-in-progress though and some things won't work in the branch... yet!

Proportional editing

Requirements for proportional editing:

  1. Need to support all blender proportional editing modes: no PE, Simple, Connected and Projected (for Meshes).
  2. Current mode obtained from the ToolSettings

Implementation:

  1. Special distance calculation routines operating on a BMesh. Must account for transforms. Upside: speed, no need to convert to an in-between data type. Downside: won't be useful for other object types, but IMO this is a good tradeoff for speed. Later can implement a switch for different object types.
  2. Put somewhere to BMesh.

Milestones

Midterm (Shapekey Internals)

Midterm targets are:

  1. Implement the Scratch shape key fully:
    1. Must be able to commit to another shape key
    2. Must be able to have the sctrach key operators to update the scratch key (Commit to Active and Restore from Active).
    3. Fully implement the editmode changes related to the introduction of the scratch shape key and general optimizations in this area (e. g. faster recalculations if there're no topology changes),
  2. Implement compression of shape keys on disk save (storing only the changed vertices in a keyblock).
  3. Implement the undo system optimization
  4. Have an alternative mix source ready (done already)
  5. Implement basic proportional editing to Blend from Shape (as in D354, but with different proportional falloff modes (projected, connected, simple)
  6. Make sure there is no user-level regression from the old modifier behavior.
  7. Have a user-level manual on new features and use cases for them.
  8. All of the above reasonably bug-free and mostly use-able and test-able.

Finals (UI Code) & Docs

  1. Extend UIList so it has tunable element height per instance
  2. Have a four-way slider widget, drawble from python (fi
  3. Code the main (special) part of the slider handler
  4. OpenMP calculations where applicable
  5. Testing, testing, testing how everything works together!
  6. Record a video tutorial showing examples of the use of the new UI.
  7. Update the Shape Key pages on the wiki.

If there’s still time...

  1. See if the new UI can be integrated with the new UI to Sculpt Mode. Shouldn’t be hard, but who knows?
  2. Evaluate the possibility to use these new features for non-Mesh objects.
  3. Add a way to specifiy custom shape evaluators (see this email