FlyPath

by Dinoj Surendran (with thanks to Mark SubbaRao and Randy Landsberg)

Download : flypathmaker.pl and the following packages it needs (note: if your machine has trouble with viewing a file called blah.pl/blah.pm for security reasons, try blah.pl/blah.pm instead.)

Introduction

Animations of a 3d model, one has to carefully design the position of the 'camera' at each timestep of the animation. The sequence of positions is called a 'flight path' or 'flypath'.

Creating a good flight path is very difficult. Some commercial visualization software kits, like Maya and Virtual Director, come with tools to aid in the design of a good flight path.

Over the last three years, we at COSMUS have made several 3d models for astronomical and other data using the visualization software Partiview (which shares the same graphical libraries as Virtual Director but is free and open source), but we have had trouble making good movies with them.

Other than a Perl module tfm.pm written by its creator (Stuart Levy) that contains some nifty utilities, Partiview does not come with tools to help create flight paths. Therefore, we had to write our own. This involves the following tasks:

Example

Suppose we want to create 30 seconds of animation at 24 frames per second. Starting with our camera at (10,20,30), we move to (1000,2e4,-1e5) with exponentially increasing velocity in a straight line for the first 20 seconds, followed by a rotation around (-1000,-1000,0) to (8888,2e4,-1e5). We always look at (0,0,0) and maintain field-of-view 45 degrees.

We wish to create, eventually, files called blah0001.png, blah0002.png, ..., blah0720.png each with resolution 640 x 480.

We create the file foo.config to have the following entries:

    outname blah
    numdec 4
    ext png
    frameoffset 1
    xsize 640
    ysize 480
    fps 24
    up 0 1 0
    maintainvertical 1

If we create the file bar.flypath with the contents:

    <FLYPATH take1 i_someplace2start m_zoomout 20 m_curve 10>
    <FLYPATH zoom4minute i_someplace2start m_zoomout 60>

    <INIT someplace2start>
      <CAMERA p_10:20:30>
      <LOOKAT p_0:0:0>
      <ROLL 0>
      <FOV 45>
    </INIT>      

    <MOVE zoomout>
      <CAMERA p_1000:2e4:-1e5>
      <SPEED EXP>
    </MOVE>

    <MOVE zoomoutlinearly>
      <CAMERA p_1000:2e4:-1e5>
    </MOVE>

    <MOVE curve>
      <CAMERA p_8888:2e4:-1e5>
      <CENTER p_-1e3:-1e3:0>
    </MOVE>

Now if we say either of

     perl ./flypathmaker.pl foo.config bar.flypath f_take1 
     perl ./flypathmaker.pl foo.config bar.flypath take1 

Then the following files are produced:

Related to just the flight path:

  • blah_take1.wf : file with 720 x 7 matrix in Partiview's WF format. Each of its 720 lines has 7 entries of the form "x y z Rx Ry Rz fov". In Partiview, saying "readpath blah_take1.wf" and "play" causes the camera path to be shown.
  • blah_take1path is a directory with a file path.cf and various speck files - running the CF file with Partiview displays the final camera path generated.
For taking/viewing regular (single monitor) images:
  • blah_take1_jump : once this file is made executable ("chmod +x blah_jump"), calling "async blah_jump" in Partiview on Linux will cause Partiview to go through the same commands as it does during movie generation without taking any pictures. Useful for checking that you've got the right path and effects and animations.
  • blah_take1_snap : once this file is made executable ("chmod +x blah_snap"), calling "async blah_snap" in Partiview on Linux will cause Partiview to go through the same path as blah_take1_jump and blah_take1.wf but taking pictures all the way, producing files blah0001.png (since frameoffset was set to 1 in test.config), blah0002.png, ..., blah0720.png.
For taking/viewing stereo images:
  • blah_take1_stereo_left/right_jump/snap : *jump and *snap files for stereo left and right views. Snap file produces blah_left/right0001.png, etc (with stereo separation -0.001 since stereo is supplied with -0.001, and focalpoint on)
  • blah_take1_stereo_snap : snapfile that takes both left and right views
For taking/viewing dome images:
  • blah_take1_dome(R/L/F/B/T/D)_(jump/snap) : files for dome; views are right, left, front, back, top, down. Glom takes the first five, Paul Bourke's cube2dome uses all six (we havent tested our outputs with cube2dome at the time of writing).
  • blah_take1_dome_snap : snapfile that takes all six views at once for each frame. Snap file produces blah_R/L/F/B/T/D0001.png, etc with 1024 x 1024 (since domesize was supplied as 2048).

Note that the .flypath file can contain details of several camera flight paths. For example, if we said

     perl ./flypathmaker.pl foo.config bar.flypath f_zoom4minute blah

Then the resulting files would be called blah_zoom4minutes* instead of blah_take1* and be for a flight path involving only the straight-line motion from (10,20,30) to (1000,2e4,-1e5) with exponentially increasing speed, and lasting for an entire 60 seconds.

Details of the .flypath format and .config options are explained next.

The FLYPATH format

A .flypath file contains details for several possible camera starting points (initial states) and of several possible segments ('moves') of the flight path. All moves are relative - they do not contain information on their starting state, though they can refer to it. A flight path is an initial state followed by at least one move - thus a .flypath file can potentially create a large variety of paths.

One can use relative variables in a .flypath file. Variables of the form t_blah are time units - they all eventually come down to seconds (not frames, since that depends on the number of frames per second). t_10s is the absolute time 10 seconds after the start of the flight path - other t_* variables will be explained later. There are also functions add(t_1,t_2) that adds two time values to make a third - saying add(t_1,-t_2) returns t_1-t_2. For now, t_2 must be an absolute time. You can also say add(t_1,x) where x is a real number in seconds.

The following tags are available. The order of tags in the .flypath file makes no difference. Within a .flypath file you can use variables instead of absolute values.

Config options

    outname blah    # final images begin with this   (default is "snap")
    numdec 4        # final images have this many numeric places (e.g. blah0310.png has 4 numeric places) 
                    #   numdec is always increased so that 10^numdec is larger than the number of frames to be generated.
                    #   default is 4
    ext png         # final images are of this format. Can be ppm or png or jpg (default)
    frameoffset 1   # final images start counting from here. In this case the first one will be blah0001.png. Default 0.
    xsize 640       # final images will be xsize x ysize pixels
    ysize 480
    fps 30          # frames per second (default 24)
    up 0 0 1        # initial value of 'up' (default 0 1 0)
    vertical 0      # do not maintain vertical (default 1) equivalent to maintainvertical
    eyesep 0.001    # stereo eye separation (sign doesnt matter)
    domesize 2048   # final size of dome images (default 2200)
    dome 0          # if 0, do not generate dome*jump/snap files. Default 1.
    stereo 0        # if 0, do not generate stereo*jump/snap files. Default 1.
    smooth 3        # smoothing parameter ; duration of half-window-size in seconds. Default 1.
    pathscale 0.01  # scaling factor for creating path.cf files (don't use unless you can't figure out psize etc). Default 1.

Kinds of Moves

MOVE tags define a 'move' of a certain duration. The duration is given in seconds, and will later be converted to frames. The following set of tags allow for any kind of movement other than orbiting the camera around a point. It includes rotation of the camera around while keeping it in the same place. Without anything in it is a 'wait for d seconds' move.

Moves that go nowhere

The simplest move is the 'wait' or empty move:

<MOVE id>      
</MOVE>

Stay there, but look somewhere else

<MOVE id>      
 <LOOKAT p_a>  
</MOVE>

Lookat is always interpolated linearly (regardless of the SPEED tag)

Stay there but tilt camera by d degrees

<MOVE id>      
 <ROLL d>  
</MOVE>

If the roll angle of the camera was x at the start of the move, it is d+x at the end of the move.

Stay there but change field of view to f degrees

<MOVE id>      
 <FOV f>  
</MOVE>

If the field-of-view of the camera was f0 at the start of the move, it is f at the end of the move.

Moves that go somewhere but end up where they started

360 degree Rotation, look at same place, no tilt

<MOVE id>      
 <CENTER p_center>
 <NORMAL p_normal>    # or  <NORMAL p_normal ONLINE>
</MOVE>

p_normal is a directional vector of nonzero magnitude, and must be orthogonal to p - p_center for any point p on the circle traced out. We refer to the line passing through the center parallel to the normal as the axis of the move.

To reverse direction in which the circle is traversed, supply the negation of p_normal.

<MOVE id>      
 <CENTER p_center>
 <NORMAL p_blah ONLINE>
</MOVE>

Sometimes, instead of supplying a direction vector p_normal, it is convenient to supply a point p_blah (other than p_center) on the axis. In this case, add ONLINE in the NORMAL tag.

n rotations, can change lookat or tilt

<MOVE id>      
 <CENTER p_center>
 <NORMAL p_normal>   # add ONLINE if p_normal is really just a point on the axis   
 <LOOKAT p_b>  
 <NUMFULLROT n>  # n is at least 1
 <ROLL d>  
</MOVE>

Moves that leave the camera in a new position

Straight Line Motion

Uniform Speed, keep looking at same point

<MOVE id>      
 <CAMERA p_a>  
</MOVE>

p_a can be an absolute point or made relative using 'vector addition' : to move p_b from the start position, p_a can be add(p_prev_100,p_b) - default is start position

Move at uniform Speed, Look somewhere else

<MOVE id>      
 <CAMERA p_a>  
 <LOOKAT p_b>  
</MOVE>

Move at uniform Speed, Look somewhere else, tilt camera

<MOVE id>      
 <CAMERA p_a>  
 <LOOKAT p_b>  
 <ROLL d>  
</MOVE>

Move at exponentially increasing speed

<MOVE id>      
 <CAMERA p_a>  
 <LOOKAT p_b>  
 <ROLL d>  
 <SPEED EXP>
 </MOVE>

At a fraction k of the way through the move, the camera is at a point p :

 
              p := p_from + (r0 + f(k)*(p_to-p_from)    

f(k) is a logarithmic or exponential function satisfying 0<=k<=1, 0<=f(k)<=1.

This applies to all the other cases above, except with the SPEED tag added.

Curves

Change position at uniform speed, look at same place, do not tilt

<MOVE id>      
 <CAMERA p_to>  
 <CENTER p_center>  
</MOVE>

The camera moves at uniform speed from p_from := p_m_curr_0 to p_to. Suppose r0 and r1 are the distances of p_from and p_to to p_center. If r0 == r1, then the camera path is the section of the r0-radius circle around p_center from p_from to p_to.

If r0 != r1 then at a fraction k (=0..1) of the way through the move, the camera is at point p such that

Change position at uniform speed, can change lookat and tilt too

<MOVE id>      
 <CAMERA p_to>  
 <CENTER p_center>  
 <LOOKAT p_b>  
 <ROLL d>  
</MOVE>

2D spiral

<MOVE id>      
 <CAMERA p_to>     # this must be different from the camera position at the start of the move
 <CENTER p_center>  
 <LOOKAT p_b>  
 <NUMFULLROT n> 
 <ROLL d>  
</MOVE>

Again, this actually allows for two possible spirals, but never mind that for now.

If n>0, then n full rotations are completed during the course of the move. n=0 is equivalent to leaving out the NUMFULLROT tag.

3D Spiral

(not fully tested yet)

With 2D spirals, the center of rotation remains the same. For 3D spirals, the center changes uniformly from the point specified in the CENTER tag to a second point along the line passing through said center in the direction specified in the NORMAL tag.

<MOVE id>      
 <CAMERA p_to>     # this must be different from the camera position at the start of the move
 <CENTER p_center>  
 <LOOKAT p_b>  
 <NUMFULLROT n> 
 <ROLL d>  
 <NORMAL p_n>     #  add ONLINE if p_n is point on axis instead of direction
 </MOVE>

Note: we used the NORMAL tag earlier, when the starting point coincided with the ending point.