# ballanim.tcl: demonstration of an animation generating frames
# Gordon Kindlmann
catch {load vtktcl}
# get the interactor ui
# "numsteps" determines the number of frames in the animation
# I'm using a number as large as 180 in order to make a smooth animation
# of what is really a very simple scene
set numsteps 180
# "imagebase" is the beginning of the filenames of all the images
# If it is "bingo" then the first frame will be saved in "bingo001.ppm"
# If the frame images should go in another directory, then that should
# be set here by putting the directory name at the beginning of the base
# The setting here assumes there is a directory called "frames"
set imagebase "frames/"
# "setup" arranges all the parameters of the various objects
# according to the control variable, which varies smoothly from 0.0 to 1.0
proc setup {t} {
# these are variables for the three lights
global lt1 lt2 lt3
# convert the control variable to an angle in radians
set angle [expr $t*2*3.141592654]
# sc controls the distance of the lights to sphere
set sc 5
# With each frame we are setting the camera location and the camera
# direction. Because the interesting part of the scene is always
# a fixed distance away, we don't have to use vtkCamera's
# SetClippingRange, but you may need to for more complicated
# camera paths
# the eyepoint travels in a circle which is slightly above z = 0
set xp [expr 3*cos($angle)]
set yp [expr 3*sin($angle)]
set zp 1
[ren1 GetActiveCamera] SetPosition $xp $yp $zp
# the normalize eye direction
set dist [expr sqrt($xp*$xp + $yp*$yp + $zp*$zp)]
set xn [expr $xp / $dist]
set yn [expr $yp / $dist]
set zn [expr $zp / $dist]
[ren1 GetActiveCamera] SetViewUp 0 0 1
[ren1 GetActiveCamera] OrthogonalizeViewUp
# the lights' position vectors are permuted versions of the eye point
# this was done just so the lights had an interesting looking path
set l2xp [expr $zp/$sc]
set l2yp [expr $xp/$sc]
set l2zp [expr $yp/$sc]
set l3xp [expr -$l2xp]
set l3yp [expr -$l2yp]
set l3zp [expr -$l2zp]
# the first light is the default light, we set it to track
# the eye point so that it acts as a "headlight"
$lt1 SetPosition $xp $yp $zp
# the second and third light are set to the same position as the balls
$lt2 SetPosition $l2xp $l2yp $l2zp
$lt3 SetPosition $l3xp $l3yp $l3zp
light2Actor SetPosition $l2xp $l2yp $l2zp
light3Actor SetPosition $l3xp $l3yp $l3zp
}
# "animate" contains the mainloop of the animation, calling "setup" for
# each time step
proc animate {} {
global numsteps imagebase
for {set i 1} {$i <= $numsteps} {incr i} {
# compute the control variable
# Note that $t will never actually reach 1.0. I did this so
# that the last frame would smoothly precede the very first
# frame, if the animation is played in a loop
# If this is not desired, use:
# set t [expr 1.0*($i-1)/($numsteps-1)]
set t [expr 1.0*($i-1)/$numsteps]
puts "step $i of $numsteps (t = $t)..."
# set up the scene and render
setup $t
renWin Render
# set the image filename using the "format" command
set name [format "%s%03d.ppm" $imagebase $i]
# uncomment these next three lines to actually save the frames
#puts " saving in $name"
#renWin SetFileName $name
#renWin SaveImageAsPPM
}
}
# basic rendering stuff
vtkRenderer ren1
vtkRenderWindow renWin
renWin AddRenderer ren1
vtkRenderWindowInteractor iren
iren SetRenderWindow renWin
iren Initialize
# a big sphere in the middle of the image
vtkSphereSource sphere
sphere SetPhiResolution 10
sphere SetThetaResolution 20
vtkPolyDataMapper sphereMapper
sphereMapper SetInput [sphere GetOutput]
vtkActor sphereActor
sphereActor SetMapper sphereMapper
[sphereActor GetProperty] SetColor 1 1 1
[sphereActor GetProperty] SetInterpolationToFlat
[sphereActor GetProperty] SetAmbient 0.0
[sphereActor GetProperty] SetDiffuse 0.4
[sphereActor GetProperty] SetSpecular 0.8
[sphereActor GetProperty] SetSpecularPower 30
# little orbiting lights
vtkPolyDataMapper light2Mapper
light2Mapper SetInput [sphere GetOutput]
vtkActor light2Actor
light2Actor SetMapper light2Mapper
[light2Actor GetProperty] SetColor 0.8 0.0 0.0
light2Actor SetScale 0.2 0.2 0.2
vtkPolyDataMapper light3Mapper
light3Mapper SetInput [sphere GetOutput]
vtkActor light3Actor
light3Actor SetMapper light3Mapper
[light3Actor GetProperty] SetColor 0.0 0.8 0.0
light3Actor SetScale 0.2 0.2 0.2
# set up renderer and window
ren1 AddActor sphereActor
ren1 AddActor light2Actor
ren1 AddActor light3Actor
ren1 SetBackground 0 0 0
renWin SetSize 256 256
# create two more lights in addition to the single default light
# without "ren1 Render", get a core dump, why?
ren1 Render
ren1 CreateLight
ren1 CreateLight
set lights [ren1 GetLights]
$lights InitTraversal
set lt1 [$lights GetNextItem]
set lt2 [$lights GetNextItem]
set lt3 [$lights GetNextItem]
$lt1 SetColor 0.7 0.7 1.0
$lt2 SetColor 0.8 0.0 0.0
$lt3 SetColor 0.0 0.8 0.0
# the very simple interface
frame .f
button .f.p -text "animate" -command animate
button .f.s -text "quit" -command exit
pack .f.p .f.s
pack .f
# make the window show the contents of the first frame
setup 0.0