Animate Your Avatar
3D Design Conference June 5, 1997
Robert W. Saint John
Integrated Data Systems
rsaintj@earthlink.net
Introduction
Instead of taking the approach of building what is traditionally known as an avatar in VRML, we'll be looking at creating something more akin to a "virtual human". At the time of this writing, the world of multiuser (MU) in VRML is in a state of flux. Although a number of people are working on standards for avatars (such as the Living Worlds and VRML Humanoid Animation working groups), nothing is firmly established within the VRML97 specification relating specifically to MU.
For the purposes of this class, therefore, we will be looking at creating a humanoid figure within the constraints of the VRML spec. The character to the left is Nutshell, a cyber android character created by author/artist/MU-guru Sue Wilcox. My thanks to her for giving me something from which to work!
For tools, I used a combination of existing bits and pieces from Viewpoint Datalabs 3D geometry libraries, trueSpace 2.0, and IDS's V-Realm Builder 2.0. This course is a 10 step exercise, and I've included the most relevant bits of code for each step here. In addition, each step has a corresponding VRML world which you are free to examine in a VRML browser such as WorldView 2.0 or Cosmo, or download and examine in a text editor.
Step 1 Geometry
I started off by assembling my bits and piecews of modeling, and converted them to VRML 2.0 by importing them into the Builder, resulting in this file. I set up the entire figure underneath a single Transform. When the files are converted to VRML, nearly everything ends up at the same level of the hierarchy, meaning that all objects are separate and unrelated. Also, none of the groups or geometry are named (or DEF'd), making the file difficult to read. This will have to be changed in our next step.
Transform { # Entire File Transform translation 0 0 0 rotation 0 1 0 0 scale 1 1 1 center 0 0 0 children Group { children [ Transform { # Beginning of the Hierarchy for the Upper Body translation 0 0 0 rotation 0 1 0 0 scale 1 1 1 center 0 0 0 children [ Transform { # Transform for the Torso translation 0 0 0 rotation 0 1 0 0 scale 1 1 1 center 0 0 0 children [ Shape { appearance Appearance { material Material { ambientIntensity 0.5 diffuseColor 0.77 0.78 0.86 emissiveColor 0.096 0.107 0.18 shininess 0.8 specularColor 1 1 1 } } geometry IndexedFaceSet { coord Coordinate { } } Group { children [ Transform {
Step 2 Label (DEF) and arrange the hierarchy
The next step is to reorganize the file in a logical fashion, and label the groups accordingly. Here is the revised VRML file. Here is a summary of the groups, how they can be labelled for readability, and a logical organization. For instance, note that all components of the left are are gouped under a single Transform. When the LEFT ARM is rotated, the LOWER LEFT ARM and LEFT HAND will follow accordingly (i.e., forward kinematics).
#VRML V2.0 utf8 BODY Transform UPPER BODY Transform TORSO Transform ARMS Group LEFT ARM Transform LOWER LEFT ARM Transform LEFT HAND Transform RIGHT ARM Transform LOWER RIGHT ARM Transform RIGHT HAND Transform NECKUP Transform NECK Transform HEAD Transform HAIR LIPS EYES LOWER BODY Transform HIPS Transform LEFT LEG Transform LOWER LEFT LEG Transform LEFT FOOT Transform RIGHT LEG Transform LOWER RIGHT LEG Transform RIGHT FOOT Transform
Step 3 Adjust the center of rotation for each limb
The majority of transformation within a humanoid figure is a matter of rotation. In most cases, the center of rotation for this figure is not related to the joints. In order to easily manipulate limbs for posing and animation, we'll want to adjust the center of rotation for each limb.
In this example of code, for instance, we'll move the center of rotation of the left upper arm from the center of the geometry, to a position up and over, where the shoulder meets the torso. The screen capture to the left shows the change being made with V-Realm Builder's Centerball Manipulator.
DEF ARMS Group { children [ DEF L_ARMXFORM Transform { translation 0 0 0 center 1 4 0 children DEF UPLARM Group { children [ Shape { appearance Appearance { material USE CHROMIUM texture DEF skin ImageTexture { url "texture/Skin.jpg" }
The hierarchy is now set up, the individual body parts are labelled, and rotations are set so that limbs will "bend" at the joints. The figure is complete at this point. Now we can move on to setting up an interface, and assign behaviors and animation to our virtual human.
Step 4 Viewpoints, Navigation and World Info
At this point, I add some miscellaneous info to my world with the WorldInfo node, and add NavigationInfo's headlight setting to TRUE (so that the figure is always illuminated,. regardless of position of the viewer). I also want to be able to set up a few controllable Viewpoints that represent the different ways we can view our character... one view from the front, one from her point of view (eye level), and a "Mario-style" above and behind view. If you open this world, you can navigate around the Viewpoints.
WorldInfo { info "by Robert Saint John" title "CyberAndroid a character by Sue Wilcox" } NavigationInfo { headlight TRUE } DEF FRONT Viewpoint { orientation 0 1 0 0 position 0 0.8 15 description "FRONT" } DEF POV Viewpoint { orientation 0 1 0 3.14159 position 0 5.2618 1.4 description "POV" } DEF ABOVE Viewpoint { orientation -0.00466 0.9687 0.2479 3.130 position -0.056 10.447 -12.764 description "ABOVE" } DEF BODY_XFORM ..
Step 5 Controls / Triggers
At this point, I want to make a VRML interface with which to manipulate the avatar's actions. I'll do this by setting up a group of shapes with their own TouchSensors, which will later act as triggers to appropriate behaviors. I use very basic shapes here, but they can just as easily be a hand for "wave", feet for "walk", etc.. A basic setup of a trigger looks like this. Note that I also moved the Walk Viewpoint within the same group as the control itself. When my figure later "walks", and I am viewing her from behind, I want the Viewpoint to be carried "along" with the avatar as well as the controls. At this point, the VRML file has the controls set up as TouchSensor triggers, but no actions have been established or routed to them.
DEF CONTROLSXFORM Transform { children DEF CONTROLS Group { children [ DEF WALK_BUTTON Transform { children [ Shape { appearance Appearance { material Material {... } } geometry IndexedFaceSet { } } DEF WALK_BUTTON_TS TouchSensor { } DEF ABOVE Viewpoint { position 4 7.2476 21 description "ABOVE" }
Step 6 Create the Wave Motion, Attach to Trigger with ROUTE
Now I can use a series of Interpolators to create a behavior... in this example, a waving motion. I use V-Realm Builder's KeyFrame Editor to set up a 3 second animation which involves raising the whole arm a bit, and rotating the forearm (and hand) back and forth in a waving motion. The code below shows the TimeSensor which sets the duration of the behavior, an example of Interpolators that govern the waving motion, and the ROUTEs that tie it all together. Note that the first of the ROUTEs is where I establish that the triangle "wave button" is a TouchSensor, which when clicked starts the Timer, which initiates the Interpolators. All of our behaviors will be set up in a fashion similar to this (note that the Builder automates the process of creating the Timer and the ROUTEs involved... this information would otherwise have to be created by handcoding).
DEF Anigroup-1-TIMER TimeSensor { cycleInterval 3 enabled TRUE loop FALSE startTime 0 stopTime 0 } DEF Anigroup-1-Intrp3-rotation OrientationInterpolator { key [ 0, 0.299492, 0.399323, 0.539763, 0.678511, 0.85956, 1 ] keyValue [ 0 0 1 0, -0.428061 0.60274 -0.6734 1.26282, -0.416494 0.285471 -0.863156 1.50759, 0.734636 -0.595966 0.324245 4.40555, -0.0916499 0.439418 -0.893595 1.59845, 0.757358 0.466027 -0.457414 0.934728, 0 0 1 0 ] } DEF Anigroup-1-Intrp21-translation PositionInterpolator { key [ 0, 0.299492, 0.678511, 1 ] keyValue [ 0 0 0, -0.292778 -0.0267401 -0.0598152, -0.235766 0.0314888 0.0253434, 0 0 0 ] } .... ROUTE WAVE_BUTTON_TouchSensor.touchTime TO Anigroup-1-TIMER.set_startTime ROUTE Anigroup-1-TIMER.fraction_changed TO Anigroup-1-Intrp3-rotation.set_fraction ROUTE Anigroup-1-Intrp3-rotation.value_changed TO LORARMXFORM.set_rotation ROUTE Anigroup-1-TIMER.fraction_changed TO Anigroup-1-Intrp21-translation.set_fraction ROUTE Anigroup-1-Intrp21-translation.value_changed TO R_ARMXFORM.set_translation ROUTE Anigroup-1-TIMER.fraction_changed TO Anigroup-1-Intrp23-rotation.set_fraction ROUTE Anigroup-1-Intrp23-rotation.value_changed TO R_ARMXFORM.set_rotation
Step 7 The Walking Script
Time to set up our next behavior, and ROUTE it to the second control, the Walk trigger. This is a more complex behavior in many ways. Number one, the motion of walking is one that obviously causes the entire avatar moving forward. But it also involves the behavior of "striding" (arms swinging, legs moving). In this step, I only want to worry about forward motion.
In addition, I want to be able to stop my character's walk at any time. VRML TouchSensors and Interpolators can trigger an animation, but they can't necessarily stop one. For instance, a VRML door can have a trigger set up on the door handle to set up an animation of the door rotating open. But a second click on the door handle will only cause the door to run through the same "dumb" animation again. To have a second click "close" the door requires the introduction of logic. Logic can easily be achieved in VRML through the use of JavaScript (or, as it is sometimes referred to in VRML, VRMLScript).
Below is an example of a simple Boolean script.... TRUE equals walking, FALSE equals stopped. The script is linked via ROUTE to the enabled field fo the Walk Timer... the clock which runs the animation of the figure moving forward is either enabled by a click, or diabled by a second click. The whole thing, in the end, is ROUTEd to the entire Avatar Transform.
Note that, in this VRML file, I placed a box in front of the avatar to know that she was moving forward in this otherwise empty environment. I will remove it later. Also, I've set up this behavior based on the VRML file in Step 5, not Step 6. Because my avatar will have multiple behaviors and multiple clocks running, it's best to set each on up separately, and cut paste the multiple behaviors together in the last step.
DEF WALK_TIMER TimeSensor { cycleInterval 15 enabled FALSE loop TRUE startTime 0 stopTime 0 } DEF WALKSCRIPT Script { url "vrmlscript: function set_boolean ( bool, eventTime ) { if ( bool == false) { return; } if ( value == true ) { value = false; } else { value = true ;} value_changed = value; } " field SFBool value FALSE eventIn SFBool set_boolean eventOut SFBool value_changed } ] ROUTE WALK_BUTTON_TS.isActive TO WALKSCRIPT.set_boolean ROUTE WALKSCRIPT.value_changed TO WALK_TIMER.set_enabled ROUTE WALK_TIMER.fraction_changed TO Anigroup-1-Intrp7-translation.set_fraction ROUTE Anigroup-1-Intrp7-translation.value_changed TO AVATAR_XFORM.set_translation
Step 8 Animating the Stride
Next I want to animate the striding motion of the character as she moves forward. I chose to do this separately from the forward motion because I want the stride governed by a different Timer. The forward motion above is set up to cycle in an interval of 15 seconds. But a striding motion can be effectively simulated in only 3 seconds, and looped. I will, however, use the same walk control trigger.
DEF STRIDE_TIMER TimeSensor { cycleInterval 3 enabled FALSE loop TRUE startTime 0 } ROUTE WALK_BUTTON_TS.touchTime TO STRIDE_TIMER.set_startTime ROUTE STRIDE_TIMER.fraction_changed TO STRIDE_Intrp22-rotation.set_fraction ROUTE STRIDE_Intrp22-rotation.value_changed TO L_ARMXFORM.set_rotation ROUTE STRIDE_TIMER.fraction_changed TO STRIDE_Intrp24-rotation.set_fraction ROUTE STRIDE_Intrp24-rotation.value_changed TO LOLARMXFORM.set_rotation
Step 9 The Kick
This step is definitely not for the timid, and probably would be a nightmare to code by hand. But using the Builder, I set up a complex animation linked to the third control, the Kick Trigger. Again using the KeyFrame Editor, I set up a sequence in which the figure slowly crouches, pulls her arms inward, pauses for dramatic effect, then quickly stands and kicks her leg up into the air. As you can imagine, nearly every limb is transformed in some fashion to pull this off. For anyone attempting to do an animation like this, I suggest two things:
One other thing I did I'll leave as a surprise. Just make sure that when you go into this world, go to the KICKCAM viewpoint before triggering the animation!
DEF KICKCAM_XFORM Transform { translation 0 0 0 children [ DEF KICKCAM Viewpoint { position -6 7.2476 21 description "KICKCAM" } DEF KICKCAM-TIMER TimeSensor { cycleInterval 3.5 enabled TRUE loop FALSE startTime 0 } DEF KICKCAM-Intrp8-translation PositionInterpolator { key [ 0, 0.55, 1 ] keyValue [ 0 0 0, 29.9673 19.2357 -30.0763, -9.25986 14.2436 -56.4332 ] } DEF KICKCAM-Intrp10-rotation OrientationInterpolator { key [ 0, 0.5, 1 ] keyValue [ 0 0 1 0, 0.100094 0.8489 0.518989 1.59133, -0.00235904 0.948588 0.316505 3.05291 ] } ] } DEF KICK_BUTTON_TouchSensor TouchSensor { }
Step 10 Combine All Elements
As I mentioned above, the final step is to combine all these behaviors into one VRML file. Pay close attention because in some cases you'll have multiple Interpolators governing different behaviors within the same Groups/Transforms. Here is the final build of the avatar, in both an uncompressed (368kb) and compressed (60kb) versions.
Case Study The Cyber Android in a World
Needless to say, the motions of waving, walking and kicking are much more interesting in the context of a world, so I've added our finished avatar to this world that looks mysteriously like Oracle's headquarters. Please don't kick the Larry. This world is available in both an uncompressed (608kb) and compressed (107kb) versions.
Thanks to Cynthia Connors at 3D Design/Miller-Freeman, Bill "Billo" McCloskey at Silcon Graphics, and Sue Wilcox for their help and assistance in putting this class together. Anything that seems weird or doesn't work here should not be attributed to them. Anything that seems weird or doesn't work (or any general comments, questions) can be sent to me directly at:
Robert W. Saint John
Administrator of VRML Development
Integrated Data Systems
1475 Folsom Street, Suite 200
San Francisco, CA 94103