Eng 591 - VR Programming - Class Outline 8

General Topics: Motion and Activity During a Simulation

Review of Known Tools for Moving Objects During a Simulation

There are several topics that were previously covered during the development of scene graphs that can also be used to provide animation during a simulation.

LOD Nodes

The level of detail nodes automatically switch geometries based on the relative location of the viewpoint. As discussed earlier, they are designed to provide increasingly detailed models as the user approaches. However there are also some examples in which the LOD node can be used for simple animations:

  • A jack-in-the-box which pops out of his box when the user approaches, and goes back inside as the user moves away. ( Or conversely, some sort of critter that "hides" as the user approaches. )
  • A door that opens as the user approaches, and closes as the user departs. This example would involve a set of instanced movable nodes as children of the LOD node, each with the same door geometry, but in different positions.

Note that these LOD animations are likely to be jerky, and that there are other methods that would provide smoother animation.

Switch Nodes

The switch node can also be used for simple animations, in a manner analogous to LOD nodes. The only real difference is that the switch is not performed automatically. The programmer must use WTswitchnode_setwhichchild and related functions to determine which child is visible at any given time. This could be in response to a key press, viewpoint location, or some other triggering event in the simulation.

Example 1: Toggling the transparency of a reactor. The switchnode "ReactorSwitch" has two children, numbered 0 and 1, representing opaque and transparent reactors, respectively. The global variable "Transparent" is of type FLAG, which is an enumerated int having possible values of TRUE ( 1 ) or FALSE ( 0 ). (The mechanism by which the global variable "ActivatedNode" is set will be covered in outline 10 on mouse actions. )

	switch( ActivatedNode ) {
   
		case REACTOR:
			Transparent = !Transparent;
			WTswitchnode_setwhichchild( ReactorSwitch, Transparent );
			break;
   
     /* Remainder of code not extracted */
   
Action Function - switch( key ), WTnode_translate, etc.

Your action function gets called each time through the simulation loop, and can be used to move objects using WTnode_translate and similar functions with which you are already familar. This can be done either in response to a key press ( or other user action ), or continuously based on physics, metaphysics, or any other laws which govern the simulation.

Example 2: Moving an object based on a key press. The following code snippet is extracted from the cardemo example program:

        WTp3 step = { 0.0f, 0.0f, 1.0f }; /* Move car 1 step forward */
   
        /* Process Keyboard Input */
   
        key = WTkeyboard_getlastkey();
   
        if( key ) {
   
            switch( key ) {
   
                case 'f':
                case 'F':
                        WTnode_translate( Car, step, WTFRAME_LOCAL );
                        break;
   
                case 'R': /* Turn car right PI / 16 degrees */
                        WTnode_axisrotation( Car, Y, PI/16.0, WTFRAME_LOCAL );
                        break;
   
                case 'L': /* Turn car left PI / 16 degrees */
                        WTnode_axisrotation( Car, Y, -PI/16.0, WTFRAME_LOCAL );
                        break;
   
                case 'r': /* Turn wheels right PI / 16 degrees */
                        /*WTnode_axisrotation( Wheels, Y, PI/16.0, WTFRAME_LOCAL );*/
                        WTnode_axisrotation( LeftWheel, Y, PI/16.0, WTFRAME_LOCAL );
                        WTnode_axisrotation( RightWheel, Y, PI/16.0, WTFRAME_LOCAL );
                        break;
   
                case 'l': /* Turn wheels left PI / 6 degrees */
                        /*WTnode_axisrotation( Wheels, Y, -PI/16.0, WTFRAME_LOCAL );*/
                        WTnode_axisrotation( LeftWheel, Y, -PI/16.0, WTFRAME_LOCAL );
                        WTnode_axisrotation( RightWheel, Y, -PI/16.0, WTFRAME_LOCAL );
                        break;
   
         /* Remainder of switch not excerpted */

Example 3: Moving an object continuously. The following code snippet is extracted from a simulation which moves molecules through space (Modified for clarity):

/*Some global and preprocessor variables for this program*/
      
#define NUMPARTICLES 100
      
static struct{
     int status;
     WTnode *node;
     WTp3 translation;
     WTp3 velocity; /*per second*/
} Particles [ NUMPARTICLES ];
      
float ElapsedTime;
      
/********************************************************************/
      
void VCR_PelletActionFcn( void ) {
	
     int i;
     static float simulationTime;
     WTp3 p3, step;
     WTq q;
	
     ElapsedTime = WTuniverse_time() - simulationTime;
     if( ElapsedTime > 1.0 ) ElapsedTime = 0.0; /* Times > 1 second are bogus */
     simulationTime = WTuniverse_time();
      
     WTviewpoint_getorientation ( Viewpoint, q );
	
     for ( i=0 ; i<NUMPARTICLES ; i++ ){
        
             WTp3_copy  ( Particles[i].velocity, step );
             WTp3_mults ( step, ElapsedTime );
             WTp3_add   ( Particles[i].translation, step, p3 );
             WTp3_copy  ( p3, Particles[i].translation );
             WTnode_settranslation ( Particles[i].node, p3 );
             WTnode_setorientation ( Particles[i].node, q );
			
/* Remainder of function not extracted */        

Task Functions

A task function is quite simply a function that the programmer writes, ( separate from the action function discussed previously ), that gets called each time through the simulation loop by WTK.
  • The function must take a single pointer as its only argument, and must not return any value. ( void return type. )
  • The pointer may point to any kind of WorldToolKit object ( node, light, sensor, etc. ), or it may point to a user-defined data structure.
  • The WTK call "WTtask_new" adds this function to the list of tasks to be run each time through the simulation loop, and determines which pointer will be passed to the function. It also assigns the task a "priority", which is used to determine the order in which tasks are called when there are multiple tasks present.
  • It is possible to assign the same task to more than one object. Likewise, it is possible to assign multiple tasks to a single object.
  • The normal order of task execution is given on page 11-2. The order can be changed using WTuniverse_seteventorder, as covered on page 2-9.

Relevant WTK Functions:

  • WTtask_new - Creates a new WTK task.
  • WTtask_remove - Removes ( deactivates ) a task, WITHOUT DELETING IT.
  • WTtask_add - Returns a task that was previously removed. ( Re-activates it. )
  • WTtask_delete - Completely deletes a task from memory.
  • WTtask_setpriority - Used to determine the order in which multiple tasks run.

Example 4: Spinning a globe using a task function:

void spinner( WTnode *target ) {  /* This is the task function */
      
     WTnode_axisrotation( target, Y, PI/16.0, WTFRAME_LOCAL );
      
     return;
}
      
/**************************************************************/
      
int main( void ) {
      
     WTnode *globe = NULL;
      
     /* Header code not shown.  Root is a global WTnode * */
      
     globe = WTmovnode_load( Root, "globe.nff", 1.0 );
      
     /* The following line specifies that the function "spinner"
        should be called once each simulation loop, that it should
        be passed the WTnode * "globe", and that it has priority 1.0
        relative to any other tasks that may be present. */
      
     WTtask_new( globe, spinner, 1.0f );
      
/* Remainder of code not shown */


Exercise:

Add animation ( motion ) to your project using a task function.