Creating Orbiting Planets with Maya's Particle Instancer

This tutorial will explore using Maya's particle system and instancer to simulate planets with several moons orbiting the Sun. With a few expressions you can control the positions and rotations of individual particles which can control geometry through Maya's instancer.

One of the first things we learn about space is that it's big - really big. We'll recreate the orbit of Jupiter and four of its major moons somewhat accurately while fudging a few numbers to make the scene more manageable. First we will need 6 spheres which will represent the Sun, Jupiter, Io, Europa, Ganymede and Callisto. 

Let's start by creating the Sun. Go to Create > NURBS Primatives > Sphere > Option box (Fig.01). If we assume that a radius of 1 is the size of Earth, the radius of the Sun would be about 109 times that which is a bit big for our scene. Set the radius to 20 and leave all other settings at their default. Name the sphere "Sun_geo".

Fig. 01

Fig. 01

The next planets can be set to a more accurate size relative to Earth. Create 5 new nurbs spheres naming and setting the radiuses to the following values:

  • Jupiter_geo = radius: 11.2
  • Io_geo = radius: 0.28
  • Europa_geo = radius: 0.24
  • Ganymede_geo = radius: 0.41
  • Callisto_geo = radius: 0.25

Now you can apply textures to the spheres if you choose (Fig.02)

Fig. 02

Fig. 02

Before we move on it is important to note how the instancer handles geometry. When geometry is instanced, it is placed at a particle using the geometry's pivot point. If the pivot is not centered then the geometry will be placed off center and will not behave properly. Also if the transforms are frozen and the geometry does not reside at the origin, the geometry will be instanced onto the particle offset the same distance the geometry is away from the origin. Since we created spheres from the menu we do not have to worry about this as their pivots are centered and transforms are frozen at the origin.

Now create an emitter by going to the dynamics module Particles > Create Emitter > Option box (Fig.03).

Fig.

Fig.

Set the type to omni. Open the outliner by going to Window > Outliner (Fig.04).

Fig.

Fig.

In the display menu check on shapes (Fig.05).

Fig.

Fig.

Click the plus sign next to particle1 and select the particleShape1 node under it (Fig.06).

Fig.

Fig.

In the attribute editor, change particle render type to numeric and click the "add attributes for current render type" button. The attribute name "particleid" will appear in a new text field underneath (Fig.07).

Fig.

Fig.

If you hit play for a few frames you will see particles with numbers associated with them being emitted (Fig.08).  It is important that the particles be emitted in order from 0 to 1 to 2 and so on. If the emitter rate is too high we run the risk of some particles being skipped which will affect the expressions we add later on. Select the emitter and set the rate to 6 (Fig.09).

Fig.

Fig.

Fig.

Fig.

Since we will only be working with 6 planets we only really need 6 particles. Select the particleShape1  node and set max count to 6 (Fig.10).

Fig. 10

Fig. 10

Now if you hit play, 6 particles numbering from 0-5 will appear over one second (Fig.11).

Fig. 11

Fig. 11

Now we will attach the geometry to the particles using the instancer. Select the Sun, Jupiter, Io, Europa, Ganymede and Callisto geometries and then the particle transform node in that order. Under particles, click instancer (replacement) (Fig.12). You can now hide your original geometries.

Fig. 12

Fig. 12

If you hit play you will notice an instance of the sun geometry is being emitted with every particle. Obviously not what we want. Select the particleShape1 node and, in the attribute editor, under the instancer (geometry replacement) tab, change the object index to Particle ID (Fig.13).

Fig. 13

Fig. 13

Now the instancer places the geometry onto the particles in the order we selected them earlier (Fig.14).

Fig. 14

Fig. 14

Since we are going to be controlling the movement of these particles with expressions we don't need any initial velocity from the emitter, which can cause problems later on. Select the emitter and set the speed to 0 in the attribute editor (Fig.15). Now if you hit play the planets will be emitted but they won't go anywhere.

Fig. 15

Fig. 15

Now it's time for expressions!

When writing expressions there are a few data types that can be used. The only one that we will use here is a vector data type. A vector is a set of data with 3 parts. It never has less or more that 3 parts to it. An example of a vector in Maya is color: R,G and B being the 3 parts. Another example, and the one we will use, is position: X, Y and Z.

We will define vector variables which the expression can reference whenever called upon. Select the particleShape1 node and under Per Particle (Array) Attributes, right click in the position text field and select Runtime Expression Before Dynamics from the drop down menu (Fig.16).

Fig. 16

Fig. 16

The first variable we will define is for the position of the Sun. This is easy to define since we do not need the Sun to move or rotate at all. To define a variable in an expression we need to do 3 things:

  1. Tell Maya what type of data it is
  2. Name the variable
  3. Assign a value to the variable.

An example of a variable would be:

data type $variable_name = value;
 
Click Here To Download Code

So a variable for the position of the sun could look like:

vector $sun_position = <<0,0,0>>;
 
Click Here To Download Code

Type this into your expression editor and click create (Fig.17).

Fig. 17

Fig. 17

Vectors are surrounded by "<<  >>" to tell Maya it is a vector. Each value is separated by a comma.

The zero's in our variable represent position of our particle system or <>.

The Sun is the only celestial body in this scene that will not be moving so we need a way to tell Maya that only the particle associated with the Sun will receive the $sun_position variable. To do that we will use an "if" statement.

"If" statements test your scene based on criteria defined by the user. They run commands if the test is passed or do nothing if the test fails. The syntax of an "if" statement looks like this.

 
if(criteria)
           {
             command;
             command;
           }
 

Click Here To Download Code

We know that the Sun is associated with the particle that has the ID of 0. So we can type:

 
if (particleShape1.id == 0)
           {
             particleShape1.position = $sun_position;
           }
 

Click Here To Download Code

Note the double "=" sign after  particleShape1.id!

This tells Maya that if particle1 has a particle with the id of 0, then Maya should apply the variable $sun_position to that particle and move it accordingly. If you hit play nothing will have changed because we assigned a variable that tells the Sun not to move.

So, how do we get Jupiter to move in a circle? Or more accurately, an ellipse? If you go back to your expression editor and change the $sun_position variable to <<5,5,5>> click edit and hit play you'll notice the sun moves 5 units in each X,Y and Z and then stops. We want Jupiter to move continually. Maya has a few terms that will do just that. One of them is "time." Time is a value that continually changes so it would make sense that we use it to move an object continually. Change the $sun_position variable to <> click edit and hit play. Now the sun moves continually but it's still not a circle or ellipse. Set the $sun_position variable back to <<0,0,0>>  for now.

So we know how to get a particle to move continuously, but we want it to go in a circle first. We'll get to an ellipse later.

We are going to use simple trigonometry to get these planets to move in an orbiting way. You don't have to know much about math because Maya has built in functions that will do everything for you. You just need to know where and when to use the functions. The two functions we are going to use are sine and cosine. Long story short, sine will give us a wave one way, cosine will give us a wave the other way (Fig.18) and when we combine them into the X and Z values of our variable we get a circle. From there it's a simple step to get an ellipse.

Fig. 18

Fig. 18




"Let's write the variable for Jupiter under the Sun variable:"

'vector $jupiter_position = <'>;
 
Click Here To Download Code

Under the sun's "if" statement, type:

 
if (particleShape1.id == 1)
          {
             particleShape1.position = $jupiter_position;
           }
 

Click Here To Download Code

If you switch to wireframe display (4 on the keyboard) you will see Jupiter moving in a circle, but it's still inside of the sun. We will have to scale our $jupiter_position variable with a little math to get the circle to be bigger.

Change the Jupiter variable to read:

vector $jupiter_position = <<100*(sin(time)),0,100*(cos(time))>>;
 
Click Here To Download Code

Fig.19 illustrates how the expression works.

Fig. 19

Fig. 19


'This will move the center of Jupiter 100 units away from the origin, giving us a nice circular orbit. To get an ellipse just change your variable to read something like:'

'vector $jupiter_position = <<100*(sin(time)),0,150*(cos(time))>>;'
' \xa0'
Click Here To Download Code

Jupiter will now follow an orbit like this (Fig.20).

Fig. 20

Fig. 20

Getting the moons to orbit Jupiter is relativity simple. We will assign the next particle its own vector variable and tell Maya to add the new variable to Jupiter's variable, which will get the particle to orbit Jupiter. We know that Jupiter's radius is 11.2 and Io's radius is .28 so the distance value in our expression (as illustrated in Fig.19) needs to be greater than 11.48. Any smaller and the planets will collide. The speed attribute needs to be faster too or Jupiter will always be between Io and the Sun. Our new expression should look like this:

 
vector $sun_position = <<0,0,0>>;
vector $jupiter_position = <<100*(sin(time)),0,150*(cos(time))>>;
vector $io_position = <<20*(sin(time*5)),0,25*(cos(time*5))>>;
 
if (particleShape1.particleId == 0)
            {
              particleShape1.position = $sun_position;
            }
 
if (particleShape1.particleId == 1)
            {
              particleShape1.position = $jupiter_position;
            }
 
if (particleShape1.particleId == 2)
            {
              particleShape1.position = ($jupiter_position + $io_position) ;
            }
 

Click Here To Download Code

You should now be able to assign variables and "if" statements for the remaining 3 moons to get an expression similar to:

 
vector $sun_position = <<0,0,0>>;
vector $jupiter_position = <<100*(sin(time)),0,150*(cos(time))>>;
vector $io_position = <<20*(sin(time*8)),0,25*(cos(time*8))>>;
vector $europa_position = <<30*(sin(time*6)),0,30*(cos(time*6))>>;
vector $ganymede_position = <<40*(sin(time*4)),0,40*(cos(time*4))>>;
vector $callisto_position = <<50*(sin(time*2)),0,50*(cos(time*2))>>;
 
if (particleShape1.particleId == 0)
            {
              particleShape1.position = $sun_position;
            }
 
if (particleShape1.particleId == 1)
            {
              particleShape1.position = $jupiter_position;
            }
 
if (particleShape1.particleId == 2)
            {
              particleShape1.position = ($jupiter_position + $io_position) ;
            }
 
if (particleShape1.particleId == 3)
            {
              particleShape1.position = ($jupiter_position + $europa_position) ;
            }
if (particleShape1.particleId == 4)
            {
              particleShape1.position = ($jupiter_position + $ganymede_position) ;
            }
if (particleShape1.particleId == 5)
            {
              particleShape1.position = ($jupiter_position + $callisto_position) ;
            }
 

Click Here To Download Code

Fig. 21

Fig. 21

If you hit play and let it go for several hundred frames you might notice the moons align every so often (Fig.21). As this most likely never happens in nature, we can go back in to our variable and add an offset value to make sure it doesn't happen. Change the moon variable to read:

 
vector $io_position = <<20*(sin((time+8)*8)),0,25*(cos((time+8)*8))>>;
vector $europa_position = <<30*(sin((time+6)*6)),0,30*(cos((time+6)*6))>>;
vector $ganymede_position = <<40*(sin((time+4)*4)),0,40*(cos((time+4)*4))>>;
vector $callisto_position = <<50*(sin((time+3)*2)),0,50*(cos((time+3)*2))>>;

Click Here To Download Code

Now at the same frame, the planets are no longer aligned (Fig.22). Let's also add variables to handle the tilt and rotation of Jupiter and its moons.

Fig. 22

Fig. 22

In order to do this we will have to add a custom attribute to particleShape1 and then tell the instancer to use this attribute for rotation. Select particleShape1 and in the attribute editor under Add Dynamic Attributes click the General button (Fig.23).

Fig. 23

Fig. 23

In the new tab type "custom_rotation" under Long name. Change Data Type to vector and Attribute Type to per particle (array) and click ok (Fig.24).

Fig. 24

Fig. 24

In the instancer section, under Rotation Options, change Rotation to custom_rotation (Fig.25).

Fig. 25

Fig. 25

Jupiter's axial tilt is 3.13 degrees and the planet rotates on that axis once every 10 hours. Working on the scale of Earth where Earth's rotation would simply equal "time", Jupiter's rotation variable will look like this:

vector $jupiter_rotation = <<0,(time/0.416),3.13>>;
 
Click Here To Download Code

Place this under the $jupiter_position variable in the expression editor to keep things organized.

Now within the curly braces "{}" of jupiter's "if" statement, type:

particleShape1.custom_rotation = $jupiter_rotation;
 
Click Here To Download Code

With a little research we find that:

  • Io's tilt is 2.21 degrees and it rotates every 42 hrs
  • Europa's tilt is 0.47 and it rotates every 85.2 hrs
  • Ganymede's tilt is 0.195 and it rotates every 171.6 hrs
  • Callisto's tilt is 0.281 and it rotates every 400.536 hrs

This brings our final expression to look like this:

 
vector $sun_position = <<0,0,0>>;
 
vector $jupiter_position = <<100*(sin(time)),0,150*(cos(time))>>;
vector $jupiter_rotation = <<0,(time/0.416),3.13>>;
 
vector $io_position = <<20*(sin((time+8)*8)),0,25*(cos((time+8)*8))>>;
vector $io_rotation = <<0,(time/1.75),2.21>>;
 
vector $europa_position = <<30*(sin((time+6)*6)),0,30*(cos((time+6)*6))>>;
vector $europa_rotation = <<0,(time/3.55),0.47>>;
 
vector $ganymede_position = <<40*(sin((time+4)*4)),0,40*(cos((time+4)*4))>>;
vector $ganymede_rotation = <<0,(time/7.15),0.195>>;
 
vector $callisto_position = <<50*(sin((time+3)*2)),0,50*(cos((time+3)*2))>>;
vector $callisto_rotation = <<0,(time/16.698),1.281>>;
 
if (particleShape1.particleId == 0)
            {
              particleShape1.position = $sun_position;
            }
 
if (particleShape1.particleId == 1)
            {
              particleShape1.position = $jupiter_position;
              particleShape1.custom_rotation = $jupiter_rotation;
            }
 
if (particleShape1.particleId == 2)
            {
              particleShape1.position = ($jupiter_position + $io_position) ;
              particleShape1.custom_rotation = $io_rotation;
            }
 
if (particleShape1.particleId == 3)
            {
              particleShape1.position = ($jupiter_position + $europa_position) ;
              particleShape1.custom_rotation = $europa_rotation;
            }
if (particleShape1.particleId == 4)
            {
              particleShape1.position = ($jupiter_position + $ganymede_position) ;
              particleShape1.custom_rotation = $ganymede_rotation;
            }
if (particleShape1.particleId == 5)
            {
              particleShape1.position = ($jupiter_position + $callisto_position) ;
              particleShape1.custom_rotation = $callisto_rotation;
            }
 

Click Here To Download Code

And our final result to look something like Fig.26 & Fig.27!

Fig. 26

Fig. 26


Fig.27

Fetching comments...

Post a comment