Events and filters
Events give you procedural control over what, when and how objects break.
During a destruction simulation, the Fracture simulator deals with large pool of different objects: input objects connected to the simulator, fragments produced during a break, passive collision objects, and many more. In Fracture, all of these different objects are handled in the same way and it is events that allow you to specify which objects will break under which conditions. Worded differently, events allow you to filter out which objects you want to break, when they should break and in what fashion.
An event consists out of one or more filters, and breaking only happens when all the filters of an event satisfy. It might help to think of filters as conditionals, conditions or if-statements. In other words, breaking only happens if all the filters test as true. (Much more about filters below)
An event might trigger in response to a collision. This type of events are called collision events. Events can also do something dictated by a keyframe or expression, for example a bomb going off at a particular time. Events like this are called "anim events". In the example below, we will using an animEvent (a bomb) to explain events further.
Types of events
Currently, Fracture has 3 types of events:
|collisionEvents||type of event gets triggered based on the collision of at least objects (i.e. a pair of objects).|
|animEvents||type of event that gets triggered by any other type of condition, except a collision|
|emissionEvents||type of event that emits particles or fluids from fragments.|
Internally in the solver, events are run in the same order as above: collision events first, then anim events, then emission events.
Video tutorials on animEvents
- FFX Fundamentals tutorials
- FFX Fast Track tutorial
- Anim events also used in the Destruction in Production tutorials.
In the screenshot above, the top-left of the events tab lists all the events. The top-right section lists all the conditions that have to be met in order for the event to trigger and an object to be broken.
These conditions are called "filters", because they filter out unwanted objects from the large pool of available ones being simulated. Filters can test the properties of objects, or test something about the scene (e.g. the current frame number). It's in the filter stack that you specify what to break and when to break it.
The idea is the same as setting up rules in an email client to filter incoming mail, and then do something with it (e.g. move it to a different folder). By this analogy, the rigid bodies are the emails, and the action of breaking an object and modifying the resulting fragments is the move-to-folder.
Another way to think of the filter stack is like a stacked sieve. All the objects the simulator knows about go in the top, and only the ones that make it through the stack get broken.
The filters each evaluate to either true or false, and an object has to pass through all the filters (all true) to be considered for breaking. The system continually tries to match objects against the filters you've specified. It's easiest to think of it as running all the conditionals on all the objects on every frame, and only occasionally do all the criteria get met.
Going back to the first image, the object filter requires that a rigid body must come from the pCubeShape1 object. This doesn't say anything about whether the rigid body is unbroken, or a fragment that resulted from a previous break. (Fragments from previous breaks go back into the pool and are able to trigger events themselves). All objects carry around data with them, and it's the data filter that specifies that the piece of data named "fractureCount" (that's attached to a rigid body) must have the value zero, i.e. the rigid body must be unbroken, not a fragment. You can see the fractureCount listed on each object under the inputs tab. When a break occurs (we'll get to that) the fractureCount data of the fragment gets 1 added to it, meaning that a fragment will be unable to pass this data filter on subsequent frames; so this event won't ever break fragments. The time filter requires that the animation time be greater than or equal to 43, meaning the bomb won't go off until that time; for every frame less than 43 that filter evaluates to false, so again the event is prevented from triggering.
When one or more objects have passed the stack of filters, the script is run, which breaks each object. The fragments from an object will inherit all of the data it was carrying around. You can see from the MEL code below that the script requests that a particular rigid body be broken, and gets back a list of fragments from the system. It then loops over those fragments, adding 1 to the fractureCount and changing the color (so you can visually identify which event an object triggered). The last thing it does is run all the newly created fragments through a list of "modifiers".
If you look back at the first image, the bomb setup has one modifier by default, a "push modifier". Modifiers are also like the actions a mail client has (e.g. move message to folder), and will edit properties of the newly created fragments at the instant they are produced by the script. The push modifier pushes fragments in a particular direction by changing their velocity. You can add more than one of these modifiers, or delete it altogether. (See https://vimeo.com/10930640).
Modifiers is also the place where data attached to fragments can be changed, for example to cause a fragments to break with a different pattern the next time it triggers an event. The script is responsible for requesting that the fragments be run through the modifier list, so that you can intermix modifiers with effects more easily achieved through code.
Aside from the push modifier,other modifiers do things like randomization of attributes or rotation, or texture lookups, etc.For a detailed list of all modifiers, go here.
Incidentally, it's worth mentioning that there is no special "bomb" concept hardwired into the system; the bomb is just a templated event setup that gives you particular filters, modifiers and code by default.
After an object is broken and the fragments have been optionally modified in some way, they enter the pool of objects known to the simulator again, and are eligible for triggering events themselves. This process repeats on each frame.
Overview of available animation events
|Modify Unbroken||changes a property of an unbroken rigid body. For example, could change the data filter and rename it to "modify fragment". Doesn't fracture anything, just runs it straight through some modifiers|
Video tutorials on collisionEvents
- FFX Fundamentals tutorials
- FFX Fast Track tutorial
- Collision events also used in the Destruction in Production tutorials.
Picture a scene with lots of objects flying around. There could be hundreds of separate collisions going on. Rigid body simulators typically process these in pairs, i.e. two objects at a time. If you want to break an object when a collision occurs, you create a collideEvent and configure the filter stack to isolate only the particular collisions that you're interested in, from the several hundreds happening in your sim.
Whereas the animEvent attempts to filter out individual objects, collideEvents try to match pairs of colliding objects. The reason for this is that you sometimes don't just want to break an object when it collides with something, you want to constrain exactly what that something is.
If you have a scene with a ball, a wall and a ground plane, there are initially 3 possible pairs: the ball hitting the wall, the ball hitting the ground, and the wall hitting the ground. If you want to break the wall when the ball hits it you specify a filter that excludes the other possibilities.
If you create a collideEvent and start adding filters, you'll notice that some of them have an extra menu with the following options: first, second, first OR second, first AND second.
First/second just means one of the two objects from the pair. It doesn't have any meaning outside this context, it's just a way of restricting which rigid body from a colliding pair you want the filter to be applied to (one of them, either or both). In the screenshot above the filter specifies that both the objects in the pair should have the data fractureCount attached to them, and the value of that data should be 0. You can see the fractureCount data initially attached to the ball and the wall (and initialized to 0) under the inputs tab. So this filter catches a collision between any two unbroken objects. This also prevents an object from breaking when it hits the ground, because ground has no data attached to it, so it will fail the test that the filter carries out. The filter excludes any collisions between the ball and the ground or the wall and the ground, and just catches the collision between the ball and the wall.
While you might intuitively think of the first object as being the ball and the second as the wall during a collision between the two, solvers don't usually give any priority to the objects they're simulating. It's just as correct to think of the first object in the pair as the wall and the second as the ball. You can swap references to first/second around, and get the same result, so long as you swap all your references in the filter list; the system will evaluate the entire stack of filters again with the objects swapped in the pair.
Suppose you configured the filters like this:
This setup says that the first object in the pair should be unbroken, and the second should be the ground plane. This would allow a break when an unbroken object hits the ground, but not when two unbroken objects hit each other. It's just as valid to say that the second object is unbroken and the first object is the ground plane. The two configurations would be equivalent.
This doesn't say anything about what that unbroken object is, it could be the ball or the wall. In practice you probably have the wall set as a passive object, and 'passive collide' switched off on the simulator globals, so there won't be any collision pairs generated between the wall and the ground. If this wasn't the case, and you only wanted to catch a collision between the ball and the ground, you could add a 3rd filter to the list which specifies that the first rigid body in the pair is from the ball:
It's the combination of saying "first" in both the data and object filters that identifies one of the two rigid bodies in the pair as being the unbroken ball. The order of the filters is irrelevant.
When at least one pair of objects passes through the filter list, the event's script is executed. If multiple collisions happen simultaneously there could be several pairs. The script breaks whichever of the objects in the pair were flagged as being breakable, provided that the fractureCount for that object hasn't reached the fractureLimit. An object also needs to have the string data "breakWith" attached to it, which holds the name of a break-geometry node that describes how it should be broken (e.g. voronoi, split-planes, crack-image). Again you can see all the data that the default script makes use of listed under the inputs tab. (The idea of rigid bodies carrying around custom data with them is the same as Maya particles having per-particle attributes that you can manipulate in expressions).
Once a break occurs, fragments are introduced, and the script will have incremented the fractureCount data on them by 1, signifying they are the result of one level of breaking. The object filter would still match a fragment that originated from the object you specified, so it's the data filters specifying the fractureCount must be 0 which prevents those fragments from breaking a second time, when they start colliding themselves. A rigid body with a fractureCount of 1 (a fragment) will fail the test applied by a data filter checking for the value 0. You'll typically setup another event to isolate those shards and start a secondary break.
Overview of available collision events
|Break Once (both unbroken)||Description|
|Break Once (once unbroken)||Description|
|Hit ground Plane||Description|
|Activate On Collision||Description|
Using Emission Events
Video tutorials on Emission Events
- FFX Fundamentals tutorials here.
- FFX Fast Track tutorial here.
- Emission events also used in this tutorial
Overview of available Emission Events
|Emit Particles on Break||Description|
Overview of available filters
Filters are logical tests applied to one or both of the rigid bodies in a colliding pair, used to describe conditions that must be satisfied in order for a collision event to be triggered. All filters must evaluate to true for the event to be triggered and the script to execute.
|Data filter||Compares the value of custom data attached to a body, with some constant value. The test may be applied to the first or second body, either (logical OR), or both (logical AND). If the body doesn't have the data attached, the filter evaluates to false.|
|Compare data filter||Compares the values of custom data on both colliding bodies.|
|Object filter||Tests if a rigid body is derived from a particular input mesh.|
|Difference filter||Returns true if the two colliding rigid bodies are from different input meshes.|
|Set filter||Evaluates to true if a body is derived from an input mesh which is a member of a particular maya set.|
|Ground plane filter||Tests if a body is the built-in ground plane.|
|Contact filter||Tests the position, normal or force (in the normal direction) at the contact point between two colliding bodies.|
|Chance filter||Returns true a certain percentage of the time.|
|Time filter||Returns true if the current time is within a range.|
|Trigger filter||lets a rigid body trigger the event once, instead of repeatedly. Without it the RB can participate in the event any number of times. The Trigger filter used for one-time edits to properties of an RB. Usage examples would be if you want to add onto the velocity on a single frame. or you want to stop fragments in mid-air. The time filter will let you limit the event to a particular period of time. The duration filter will restrict how long the event can keep executing after the first time it runs (whenever than happens to be). The trigger filter will let things run once. Without any of the aforementioned filters an event will trigger repeatedly.|
More on filters and attributes: Link
Overview of available modifiers