Here's an example of the Maya Bullet integration implementing the desired behavior.
Notice how I can manipulate the cube on the start frame, and have it continue manipulating the cube on subsequent frames. Starting from wherever I left it.
Requirements
- Must work with Parallel Evaluation
- Must work with Cached Playback
- Must work with
mayapy
, i.e. without a GUI
These are the things I've tried so far.
# | Attempt | Description |
---|---|---|
1 | Attribute Affects | The most basic and flawed, cyclic dirty propagation |
2 | Output Value I - Naive | Trust the datablock.outputValue |
3 | Output Value II - Start, First, Second | Split evaluation over three frames |
4 | Output Value III - Pull from Callback | Bruteforce pulling to keep outputValue up-to-date |
5 | MDGContext | Travel back in time to read start frame |
6 | Internal Value | Does internalValue help? |
7 | Mouse Up Event | Bruteforce keeping outputValue up-to-date with mouse events |
8 | Output Value IV - By Force | Bruteforce update of outputValue during rendering |
9 | Matrix, not World Matrix | Can we pull pCube1.matrix in response to pulling pCube1.translateX ? |
10 | Matrix + Parent Matrix | Can we pull on .parentMatrix whilst evaluating .translateX ? |
11 | MFnTransform | Does this operate differently from pulling plugs? |
12 | Solver & Rigid I | Separate solver and rigid |
13 | Solver & Rigid II | onWorldMatrixModified |
14 | Solver & Rigid III | Vanilla, manual initial state from Python |
15 | Solver & Rigid IV | Vanilla, working in DG and Parallel, no initial state |
16 | Basic Initial State | Can we pull initial state on the start frame, ahead of evaluation? |
17 | Advanced Initial State | Animated initial state and transitioning to and from "passive" mode. |
For completeness, here's what it looks like in the production project. It won't all make sense, hence the minimal reproducible associated to every attempt.
- I'd like to change initial state for one object
- And I'd like to not dirty the child transforms when that happens
I cannot make changes to the initial state, without explicitly writing to it via script. A is the goal user interaction, B is the current workaround.
Hierarchy is not maintained when parent moves.
Here's an example of the Maya Bullet integration implementing the desired behavior.
Here's what the network looks like.
.currentTime
Likely used to dirty the output plugs.inParentInverseMatrix
This is needed to translate the worldspace Bullet transform into a localspace transform, and likely isn't used for the actual simulation..inWorldMatrix
This looks to be where it gets the rest position of the box.pivotTranslate
Likely also used post-simulation, to convert worldspace into the local Maya transform, respecting the rotate pivot..solverInitialized
Interestingly the solver telling the rigid the solver has been initialised, rather than the rigid passing data to the solver for its initial state on the first frame..solverUpdated
Likewise, a connection from solver to rigid. I haven't made sense out of these yet. What can the rigid do with that information?
The most noticeable bit is pCube1.worldMatrix[0]
connecting to bulletRigidBodyShape1.inWorldMatrix
. Which can also be seen from the AE.
Here's a schematic for clarity.
___________________________________________________
| __________________ |
| | | |
| | worldMatrix[0] o--`
| __________________ | |
| | | | |
`-->o inWorldMatrix | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| outputTranslate o----->o translate |
| | |__________________|
|__________________|
transform
My plug-in
Sidenote, the Bullet behaviour also applies to hierarchies of objects. This suggests to me that the .outSolvedTranslate
isn't actually dirtied when inWorldMatrix
is dirtied. Bullet isn't doing anything here, just standing by.