3D Transformations

This lab is an introduction to 3D transformations and coordinate space nesting.

We will be using a Python based application only available in R105.

If you are familiar with the CLI and Unix program development, apologies in advance for these long explanations.

Log on to Linux and start a terminal window. Change to your computer graphics directory and download file solar.py3d.

For this exercise you should have a text editor and the Python 3D viewer application, pfpy3d, running side by side. Use something like:

$ gedit solar.py3d &
$ pfpy3d solar.py3d

(You can use kate, emacs, or whichever editor you prefer.)

Run the editor in the background rather than the viewer because the viewer prints error messages to the terminal and you will need to read them. If you get a pfpy3d: command not found message, ask your tutor for help.

The mouse wheel, or up/down arrow keys, move forward and back. Use them to back away until you can see a yellow sphere. The left mouse button, or left/right arrow keys, rotate the scene. The page up and page down keys move you vertically. Try changing your viewpoint until you can see two spheres.

In this lab you will be making a series of small changes to the downloaded file and observing the effects. Often you will want to restore the original contents afterwards. This can be done in any way you find convenient, such as

Simple changes

Disclaimer: although the names of real planets and moons are used in this lab because it's easier and more distinctive than "planet number one", the distances and diameters are imaginary. This is not an accurate model of the solar system.

Read through solar.py3d without worrying about the exact details of each line. (There is some online documentation.)

Find the section that draws the sun, and try changing the diameter value. Save the file: the display should update to match.

Do not close the viewer program and restart it each time you make a change. The viewer will automatically update every time you Save in the editor.

Find the line near the start

Day = 0
Try changing it to 10, 20, etc, saving each time and checking the display.

Find the section that draws the Earth. Swap the order of the rotate and translate matrices. Try changing the day again: what happens?

Swap them back to the original once you've worked out why.

Colors in computer graphics are usually described as Red Green Blue, RGB, numeric triples. These are floating point values between 0.0 and 1.0, from none to maximum intensity. The color for Earth is given as (0, 0, 1). Try changing it to (1,0,0) (0,1,0) and (0.5, 0.5, 0.5). (Don't spend too much time on this.)

What do you think would happen if you changed it to (0, 0, 0)? Try it and see if you were right.

Separating transformations

Add Mars to your solar system. The drawing code should be very similar to that used for the Earth, but Mars is red, smaller in diameter (1) and further away from the sun (40).

If you swap the rotate and translate matrices for this planet, what would happen to the Earth?

In the Main section at the end, cut the code that draws Mars and paste it before the Earth rather than after. The scene should not change: if it does, you are not properly using begin and end to save the transformations around your Mars code and you need to fix it.

Change the day again. Both planets will be in a straight line. Since Mars really has a longer year, 687 days, this isn't right. Find the line

r = Day
and change it to
r = Day / 687.0 * 360.0
(The ".0" is needed to make Python use a floating point divide instead of an integer which would nearly always give zero.) This is a common interactive or animation technique, where many objects in a 3D world derive their position or other values relative to a master "clock" variable.

Nested transformations

Add Jupiter to your solar system. The diameter is 5, the orbit distance from the Sun is 60, the year is 4332 days. Make it green or otherwise different.

Next, add a moon, Ganymede, with an orbit distance of 10 from Jupiter and a diameter of 0.5. It orbits once every 7.2 days. This orbit is relative to Jupiter, not the sun, so needs to be nested inside the Jupiter drawing code. It should look something like

begin rotation, translation for Jupiter
draw Jupiter
begin rotation, translation for Ganymede
draw Ganymede
end Ganymede transforms
end Jupiter transforms

(Many C/Java programmers including myself like to indent the code between lines that begin or end matrices. Don't do that here: the Python interpreter will complain.)

Check your transformations by increasing the day by small amounts: 1, 2, ... Jupiter should barely move, but Ganymede should change position around it.

Add another moon, Callisto. Diameter is 0.4, orbital distance 13, orbits every 16 days. Make sure that it is moving independently of Ganymede.

Add a space station in orbit around Ganymede, not around Jupiter. How many levels of transformation nesting are needed for this space station?

Optional Advanced

Add Saturn to the solar system (distance 90, orbits every 10,760 days), but using different code. You will need to create a the scale matrix.

begin rotation
create sphere diameter 1
begin translation
begin scale(4)
draw the sphere
end ... as needed
Note that although the sphere is created with a diameter equal to that of the Earth, when drawn it appears larger because of the effect of the scaling matrix.

Now here's the interesting bit: what happens if you begin the scale matrix before the translation?

Writing Less Code

Recognising blocks of similar code and replacing it with functions or methods is an important skill for graphics programming. In this case, the code for positioning each planet or moon is almost identical.

At the beginning of the Python code, add these two functions (equivalent to Java static methods):

 
def beginOrbit (year, radius):
    scene.begin(matrix.rotate(Day / year * 360, (0,1,0)))
    scene.begin(matrix.translate(radius, 0, 0))

def endOrbit ():
    scene.end()
    scene.end()
 
then replace the scene.begin and scene.end around one of your planets with
 
beginOrbit(days-in-year, orbital-radius)
...
endOrbit()
 
Once you've got it working for one planet, do the same for all the others.

This applies to all graphics programs, whether written in Python, C, or Java. Any time you find yourself copying and pasting code, think about writing a function or method instead.