Difference between revisions of "Bouncing Ball Script (Transform Channels)"

From The Foundry MODO SDK wiki
Jump to: navigation, search
Line 100: Line 100:
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
  lx.command( "item.channel", channel="pos.X", value="1.3", item=pivotID )
 
  lx.command( "item.channel", channel="pos.X", value="1.3", item=pivotID )
 +
</syntaxhighlight>
 +
}}
 +
 +
== Bouncing Ball Example ==
 +
This example perl script by Arnie Cachelin creates a simple bouncing ball style animation by creating keyframes on the position transform item of the currently selected item.
 +
 +
{{TabbedArea
 +
|Perl|
 +
<syntaxhighlight lang="perl">
 +
#perl -- Bouncer.pl
 +
# Arnie Cachelin, Luxology LLC, Copyright 2007-2008
 +
# A Simple Dynamics Script -- Physics 101 equations of
 +
# motion in one dimension
 +
 +
# This version specializes in the trajectory of a dropped or
 +
# launched object which bounces.
 +
# To use this, select the object which is already at the time
 +
# and place at its motion should start.
 +
 +
 +
# User Input animation parameters
 +
my $v0=0;          # initial speed (m/s)
 +
my $time = 11.0;    # length of simulation (in seconds)
 +
my $ymin = 0.0;    # ground/wall position, where bounces would occur (m)
 +
my $elastic = 0.8;  # elasticity: percent energy kept per bounce
 +
my $fstep = 0.2;    # number of frames between keys
 +
 +
# physical constants
 +
my $g=9.8;          # gravity on earth = 9.8 m/s^2 DOWN
 +
my $a=-$g;          # accleration (m/s^2) negative, since UP is positive
 +
 +
lxtrace(1);        # Use this for debugging
 +
 +
# get selected locator-type item
 +
my $item = lxq( "query sceneservice selection ? locator" );
 +
 +
# get its position transform item
 +
my $xfrm = lxq ("query sceneservice item.xfrmPos ? $item");
 +
 +
if ($xfrm eq "") {
 +
    # Position transform item does not exist; create it
 +
    lx ("transform.add type:pos");
 +
    $xfrm = lxq ("query sceneservice item.xfrmPiv ? $id");
 +
}
 +
 +
# select the item in sceneservice for subsequent channel queries
 +
my $xnm = lxq ("query sceneservice item.name ? $xfrm");
 +
 +
my $chan = 5;                    # pos.Y.  For safety, this should be looked up, not just assumed to always be '5'
 +
my $tf = lxq ("time.range scene out:?");
 +
$ti = lxq ("select.time ?");    # starting time: Now!
 +
my $time = $tf - $ti;            # final time
 +
my $val = lxq ("query sceneservice channel.value ? $chan");
 +
my $fps = lxq ("time.fpsCustom ?");
 +
my $dt = $fstep / $fps;          # add keys every other frame
 +
my $nf = $time * $fps / $fstep;  # number of frames
 +
 +
my $y0 = $val - $ymin;          # initial position
 +
 +
#lxout ("Time = $time ( $nf frames at $fps fps ) from $ti s to $tf s by $dt s");    # Use this for debugging
 +
 +
 +
# calculation section, where plenty of other important things are derived from the inputs
 +
 +
# maximum height reached, (where vtop==0)
 +
my $ytop = ((0.5 * $v0 * $v0)/$g) + $y0;
 +
 +
# time to reach ytop from y0
 +
my $ttop = ($v0)/$g;
 +
 +
# time to hit ground from ytop
 +
my $tbounce = sqrt (($ttop * $ttop) + 2 * $y0 / $g);
 +
 +
# speed at bounce, deduced from energy conservation
 +
my $vbounce = sqrt (($v0*$v0) + 2*$g*$y0);
 +
 +
#lxout ("Top at: $ttop s ($ytop m) Bounces $tbounce s later");
 +
 +
 +
# add initial key at current time
 +
lx ("channel.key mode:add channel:{$xfrm:$chan}");
 +
lx ("select.channel mode:set channel:{$xfrm:$chan}");
 +
lx ("channel.interpolation linear");
 +
lx ("channel.value mode:set value:{$val}");
 +
 +
my $tscene = $ti;
 +
my $tt;
 +
my $y;
 +
 +
# time of next bounce relative to t0
 +
my $tb = $ttop + $tbounce;
 +
#lxout ("Bounce at = $tb s Top: $ttop s ($ytop m) ");
 +
 +
$t = 0;
 +
for(my $i = 0; $i < $nf; $i++) {
 +
    $t += $dt;
 +
 +
    # initial position
 +
    $val = $y0;
 +
 +
    # plus initial constant velocity contribution
 +
    $val += $v0 * $t;
 +
 +
    # plus acceleration contribution $val+=0.5*$a*$t*$t;
 +
    if ($t>$tb) {
 +
          # add key directly at bounce point!
 +
          $tt = $tscene + $tb;
 +
          lx ("select.time $tt");
 +
          lx ("channel.key mode:add");
 +
          lx ("channel.value mode:set value:{$ymin}");
 +
          #lx ("key.break slope");
 +
          #lx ("key.slopeType linearIn in");
 +
          #lx ("key.slopeType linearOut out");
 +
          # reset motion params
 +
          $y0 = $ymin;        # start at bounce point
 +
          $v0 = $vbounce;      # new initial velocity
 +
          $tscene += $tb;      # reset time so t=0 at bounce
 +
          $t=$t-$tb;          # next key 't' a bit after bounce
 +
          # start at bounce point
 +
          # new initial velocity
 +
          # reset time so t=0 at bounce #nextkeytabitafterbounce
 +
 +
          # attenuate t,v
 +
          $tbounce = sqrt ($elastic) * $tbounce;
 +
          $vbounce = sqrt ($elastic) * $vbounce;
 +
 +
          # time up and time down
 +
          $tb =  2 * $tbounce;
 +
 +
          # recompute key after bounce
 +
          $val = $y0;
 +
 +
          # plus initial constant velocity contribution
 +
          $val += $v0 * $t;
 +
 +
          # plus acceleration contribution
 +
          $val+=0.5*$a*$t*$t;
 +
    }
 +
 +
    # use absolute time, not per-bounce time
 +
    $tt = $tscene + $t;
 +
 +
    # convert relative y back to absolute
 +
    $y = $val + $ymin;
 +
 +
    # set time, add and set key
 +
    lx ("select.time $tt");
 +
    lx ("channel.key mode:add");
 +
    lx ("channel.value mode:set value:{$val}");
 +
}
 +
</syntaxhighlight>
 +
|Lua|
 +
<syntaxhighlight lang="lua">
 +
 +
</syntaxhighlight>
 +
|Python|
 +
<syntaxhighlight lang="python">
 +
 
</syntaxhighlight>
 
</syntaxhighlight>
 
}}
 
}}

Revision as of 12:24, 31 March 2012

modo takes a somewhat unique approach to item transforms. Rather than include the transforms as part of the item itself, the item is linked to secondary transform-specific items. This article explains how transform channels work and demonstrates them with script that creates a bouncing ball.

Transform Channels

Every item in modo has channels, and these are the primary manner in which item state is exposed for editing. Some channels are hidden from the user, or represent [Common Datatypes#Complex Datatypes|complex datatypes]] such as mesh stacks and item links. In general, scripts deal only with simple datatypes, notably numeric and string channels.

As described previously, most item channels can be walked through the Sceneservice ScriptQuery interface, and can be read or set using the Item.channel command. Transform channels, however, are a little different.

Transform Items

Locator item types (which includes Mesh, Camera, Light and so on) do not directly have their own position, rotation and scale channels. Instead, they have links to transform items that handle these properties. This allows multiple items to share the same transform items, and to save memory by not creating the transform items and associated channels when they aren’t required.

This unique system complicates how the position, rotation and scale of an item are read. You can’t directly ask the item for its channels, since they aren't on that item. Instead, you must ask the item for its transform item, and then query that item's channels. The sceneservice ScriptQuery interface provides a series of item.xfrm??? attributes to simplify getting the IDs of the transform items.

Attribute Transform
item.xfrmItems (all)
item.xfrmPos Position
item.xfrmRot Rotation
item.xfrmScl Scale
item.xfrmPiv Pivot
item.xfrmPivC Pivot Compensation

These each require an item ID as a selector, which can be obtained via the item.id attribute of sceneservice. The item.xfrmItems attribute returns a list of all transform items currently used by the selector item. For example, a mesh item might return the following:

scale021
rotation022
translation023

You can also directly request a transform item by using one of the other item.xfrm??? attributes. These simply return the item ID string, or an empty string if there is no linked transform item.

As you can see from the table above, there are currently five types of transform items. The position, rotation and scale transforms should be obvious. Pivot defines a "pivot point", which defines the position at which the rotation transform is centered. Pivot compensation is the inverse of the pivot transform; when you move the pivot of an item that has been scaled or rotated, a compensation transform is calculated to ensure that the item remains in the same wold position.

Creating Transform Items

If the transform you want to manipulate does not yet have a matching item, you need to create it first. This example shows how to check for the pivot transform item, and if it is not found, create it through the transform.add command.

TabbedArea

Once you have the item ID, you can query it as normal through the item.channel command or through the sceneservice interface. Since item.channel works on the current item selection, you need to select the transform item first with select.item, or you can pass it as the item argument. This example sets the X position of the pivot item to 1.3 meters.

TabbedArea

Bouncing Ball Example

This example perl script by Arnie Cachelin creates a simple bouncing ball style animation by creating keyframes on the position transform item of the currently selected item.

TabbedArea