/***********************************************************************/        /***********************************************************************/
/* FreeVR OpenGL tutorial example 5: ex5_joystick.c                    */        /* FreeVR OpenGL tutorial example 6: ex6_text.c                        */
/* Last tested with: FreeVR version 0.6a                               */        /* Last tested with: FreeVR version 0.6a                               */
/* Last Modified: January 11, 2007                                     */        /* Last Modified: January 11, 2007                                     */
/* URL: http://freevr.org                                              */        /* URL: http://freevr.org                                              */
/***********************************************************************/        /***********************************************************************/
/* This example code demonstrates a VR application program that        */        /* This example code demonstrates a VR application program that        */
/*   examines the two valuator inputs that comprise the joystick       */        /*   reports some of the internal world data textually via a very      */
/*   motion and use these values to fly the green pyramid around the   */        /*   simple 2D/raster font.  (We'll do 3D text in a later example.)    */
/*   X-Z plane.                                                        */     
/*                                                                     */        /*                                                                     */
/*                                                                     */        /*                                                                     */
/* The new features of this example include:                           */        /* The new features of this example include:                           */
/*   - FreeVR Library functions, variables, and macros:                */        /*   - FreeVR Library functions, variables, and macros:                */
/*      * vrGetValuatorValue() [used for joystick interaction]         */        /*      * type vrRenderInfo                                            */
                                                                                 /*      * vrRenderText(rendinfo, )                                     */
/*                                                                     */        /*                                                                     */
/*                                                                     */        /*                                                                     */
/* Considerations:                                                     */        /* Considerations:                                                     */
/*   - User modifications that are based on constant polling of an     */        /*   - This is our first exposure to the vrRenderInfo type.  But we    */
/*       input (in this case joystick valuators), should be adjusted   */        /*       will be using it in most of the rest of the examples.  For    */
/*       to compensate for the speed of the machine.  This is done     */        /*       the most part, the application programmer does not need to    */
/*       by calculating the amount of time that passes between         */        /*       worry about the details of this type & renderer argument.     */
/*       simulation frames (a time-delta).                             */        /*       It can best be thought of as simply a conduit through which   */
/*   - Interactions from some valuators can greatly benefit from the   */        /*       internal VR state information can be passed from the          */
/*      use of an epsilon that creates a "null region" around the zero */        /*       overarching render loop process to VR rendering operations    */
/*      point to ensure the input is intended.  Subtracting that same  */        /*       that require this external state knowledge to work properly.  */
/*      epsilon value from the operation is also generally a good thing*/        /*   - Textual output is often an important means of communicating     */
                                                                                 /*       with the user.  Certainly not every VR application requires   */
                                                                                 /*       the ability to put text about the state of the world in front */
                                                                                 /*       of the user.  However, it is always a useful feature to have  */
                                                                                 /*       when debugging a VR application (since the user/tester will   */
                                                                                 /*       generally not be sitting at the terminal to see printf-style  */
                                                                                 /*       messages).                                                    */
                                                                                 /*   - 2D raster (aka bitmap) text is generally less useful in a       */
                                                                                 /*       virtual reality experience, because the bitmap is a fixed     */
                                                                                 /*       size (number of pixels), and will be rendered the same no     */
                                                                                 /*       matter how close the user gets to it.  The best way to        */
                                                                                 /*       overcome this shortcoming is to always place the text at a    */
                                                                                 /*       location where this won't be noticed by the user.  In the     */
                                                                                 /*       case of a CAVE-like environment, this means placing the text  */
                                                                                 /*       to be located on the plane of one of the screens.  For a      */
                                                                                 /*       head-based system, the text should be on a head-up type       */
                                                                                 /*       widget that always remains a fixed distance from the user.    */
/*                                                                     */        /*                                                                     */
/* This application uses the files:                                    */        /* This application uses the files:                                    */
/*   - shapes.c  (Bill Sherman's simple OpenGL shapes)                 */        /*   - shapes.c  (Bill Sherman's simple OpenGL shapes)                 */
/*                                                                     */        /*                                                                     */
/* Copyright 2010, William R. Sherman, All Rights Reserved.            */        /* Copyright 2010, William R. Sherman, All Rights Reserved.            */
/*   In particular, the right to republish this file in whole or in    */        /*   In particular, the right to republish this file in whole or in    */
/*   part, in any form (including electronic forms) is prohibited      */        /*   part, in any form (including electronic forms) is prohibited      */
/*   without written consent of the copyright holder.                  */        /*   without written consent of the copyright holder.                  */
/***********************************************************************/        /***********************************************************************/
#include <stdio.h>      /* needed for printf(), etc. */                          #include <stdio.h>      /* needed for printf(), etc. */
#include <stdlib.h>     /* needed for exit() */                                  #include <stdlib.h>     /* needed for exit() */
#include <signal.h>     /* needed for signal() and its arguments */              #include <signal.h>     /* needed for signal() and its arguments */
#include <unistd.h>     /* not sure why/whether this is needed */                #include <unistd.h>     /* not sure why/whether this is needed */
#include <math.h>       /* needed for sin(), cos(), fabs() & copysign() */       #include <math.h>       /* needed for sin(), cos(), fabs() & copysign() */
 
#include <GL/gl.h>      /* needed for all OpenGL functions & values */           #include <GL/gl.h>      /* needed for all OpenGL functions & values */
 
#include "freevr.h"     /* needed for FreeVR functions & values */               #include "freevr.h"     /* needed for FreeVR functions & values */
 
/* Define a structure for holding a database of the virtual world. */            /* Define a structure for holding a database of the virtual world. */
typedef struct {                                                                 typedef struct {
                float           obj1_x, obj1_y, obj1_z;         /* location o                    float           obj1_x, obj1_y, obj1_z;         /* location o
                float           obj2_x, obj2_y, obj2_z;         /* location o                    float           obj2_x, obj2_y, obj2_z;         /* location o
                float           obj3_x, obj3_y, obj3_z;         /* location o                    float           obj3_x, obj3_y, obj3_z;         /* location o
        } WorldDataType;                                                                 } WorldDataType;
 
 
/****************************************************************************    /****************************************************************************
/* These function declarations are very important -- they will be used as arg    /* These function declarations are very important -- they will be used as arg
void    exit_application(int signal);           /* cleanly close down operati    void    exit_application(int signal);           /* cleanly close down operati
 
void    init_gfx(void);                         /* initialize the graphics       void    init_gfx(void);                         /* initialize the graphics   
void    draw_world(WorldDataType *wd);          /* graphically render the wor    void    draw_world(WorldDataType *wd, vrRenderInfo *rendinfo); /* render the 
void    init_world(WorldDataType *wd);          /* initialize the world param    void    init_world(WorldDataType *wd);          /* initialize the world param
void    update_world(WorldDataType *wd);        /* perform the world simulati    void    update_world(WorldDataType *wd);        /* perform the world simulati
 
 
/***********************************************************************/        /***********************************************************************/
main(int argc, char* argv[])                                                     main(int argc, char* argv[])
{                                                                                {
        WorldDataType   *wd;                                                             WorldDataType   *wd;
 
        /**********************************/                                             /**********************************/
        /*** initialize FreeVR routines ***/                                             /*** initialize FreeVR routines ***/
        /**********************************/                                             /**********************************/
        signal(SIGINT, SIG_IGN);                                                         signal(SIGINT, SIG_IGN);
        vrConfigure(&argc, argv, NULL);         /* allow CLAs to affect confi            vrConfigure(&argc, argv, NULL);         /* allow CLAs to affect confi
        vrStart();                                                                       vrStart();
        signal(SIGINT, exit_application);                                                signal(SIGINT, exit_application);
 
        /********************/                                                           /********************/
        /* create the world */                                                           /* create the world */
        wd = (WorldDataType *)vrShmemAlloc0(sizeof(WorldDataType));                      wd = (WorldDataType *)vrShmemAlloc0(sizeof(WorldDataType));
        init_world(wd);                                                                  init_world(wd);
 
        /***************************************/                                        /***************************************/
        /* set up the FreeVR library functions */                                        /* set up the FreeVR library functions */
        vrFunctionSetCallback(VRFUNC_DISPLAY_INIT, vrCallbackCreate(init_gfx,            vrFunctionSetCallback(VRFUNC_DISPLAY_INIT, vrCallbackCreate(init_gfx,
        vrFunctionSetCallback(VRFUNC_DISPLAY, vrCallbackCreate(draw_world, 1,            vrFunctionSetCallback(VRFUNC_DISPLAY, vrCallbackCreate(draw_world, 1,
 
        /********************************************************/                       /********************************************************/
        /* define the application name, authors, controls, etc. */                       /* define the application name, authors, controls, etc. */
        vrSystemSetName("FreeVR tutorial: example 5 -- joystick");                       vrSystemSetName("FreeVR tutorial: example 6 -- simple text");
        vrSystemSetAuthors("Bill Sherman");                                              vrSystemSetAuthors("Bill Sherman");
        vrSystemSetExtraInfo("An example to show basic joystick input");                 vrSystemSetExtraInfo("An example to show basic 2D text rendering");
        vrSystemSetStatusDescription("Application running fine");                        vrSystemSetStatusDescription("Application running fine");
        vrInputSet2switchDescription(0, "Terminate the application");                    vrInputSet2switchDescription(0, "Terminate the application");
        vrInputSet2switchDescription(1, "Raise the green pyramid");                      vrInputSet2switchDescription(1, "Raise the green pyramid");
        vrInputSet2switchDescription(2, "Reset the green pyramid");                      vrInputSet2switchDescription(2, "Reset the green pyramid");
        vrInputSet2switchDescription(3, "Lower the green pyramid");                      vrInputSet2switchDescription(3, "Lower the green pyramid");
        vrInputSetValuatorDescription(0, "Move the green pyramid in the Z-pla            vrInputSetValuatorDescription(0, "Move the green pyramid in the Z-pla
        vrInputSetValuatorDescription(1, "Move the green pyramid in the X-pla            vrInputSetValuatorDescription(1, "Move the green pyramid in the X-pla
 
        /***************************/                                                    /***************************/
        /*** do world simulation ***/                                                    /*** do world simulation ***/
        /***************************/                                                    /***************************/
        /* run until terminate key is pressed (Escape by default) */                     /* run until terminate key is pressed (Escape by default) */
        while(!vrGet2switchValue(0)) {                                                   while(!vrGet2switchValue(0)) {
                vrFrame();                                                                       vrFrame();
                update_world(wd);                                                                update_world(wd);
        }                                                                                }
 
        /*********************/                                                          /*********************/
        /*** close up shop ***/                                                          /*** close up shop ***/
        /*********************/                                                          /*********************/
        exit_application(0);                                                             exit_application(0);
}                                                                                }
 
 
/********************************************************************/           /********************************************************************/
/* exit_application(): clean up anything started by the application */           /* exit_application(): clean up anything started by the application */
/*   (forked processes, open files, open sockets, etc.)             */           /*   (forked processes, open files, open sockets, etc.)             */
/********************************************************************/           /********************************************************************/
void exit_application(int signal)                                                void exit_application(int signal)
{                                                                                {
        vrExit();                                                                        vrExit();
 
        exit(0);                                                                         exit(0);
}                                                                                }
 
 
 
/* ----------------8<-----------------8<-----------------8<----------------*/    /* ----------------8<-----------------8<-----------------8<----------------*/
/* In a non-example applicatation, the following would be a separate file. */    /* In a non-example applicatation, the following would be a separate file. */
 
                /**********************************/                                             /**********************************/
                /**********************************/                                             /**********************************/
                /** The World Simulation section **/                                             /** The World Simulation section **/
                /**********************************/                                             /**********************************/
                /**********************************/                                             /**********************************/
 
 
/**************************************************************/                 /**************************************************************/
/* init_world(): create the initial conditions of the dynamic */                 /* init_world(): create the initial conditions of the dynamic */
/*   simulation.                                              */                 /*   simulation.                                              */
/**************************************************************/                 /**************************************************************/
void init_world(WorldDataType *wd)                                               void init_world(WorldDataType *wd)
{                                                                                {
        /* initial location of object 1 - a blue cube */                                 /* initial location of object 1 - a blue cube */
        wd->obj1_x = -2.0;                                                               wd->obj1_x = -2.0;
        wd->obj1_y =  5.0;                                                               wd->obj1_y =  5.0;
        wd->obj1_z = -5.0;                                                               wd->obj1_z = -5.0;
 
        /* initial location of object 2 - a red pyramid */                               /* initial location of object 2 - a red pyramid */
        wd->obj2_x =  2.0;                                                               wd->obj2_x =  2.0;
        wd->obj2_y =  5.0;                                                               wd->obj2_y =  5.0;
        wd->obj2_z = -5.0;                                                               wd->obj2_z = -5.0;
 
        /* initial location of object 3 - an upside-down green pyramid */                /* initial location of object 3 - an upside-down green pyramid */
        wd->obj3_x =  0.0;                                                               wd->obj3_x =  0.0;
        wd->obj3_y =  5.0;                                                               wd->obj3_y =  5.0;
        wd->obj3_z = -5.0;                                                               wd->obj3_z = -5.0;
}                                                                                }
 
 
/*********************************************************************/          /*********************************************************************/
/* update_world(): This version still has a little action, and a     */          /* update_world(): Basic world with simple interactions.             */
/*   little more interactivity.                                      */       
/*********************************************************************/          /*********************************************************************/
void update_world(WorldDataType *wd)                                             void update_world(WorldDataType *wd)
{                                                                                {
#define JS_EPSILON      0.125                           /* the dead-zone befo    #define JS_EPSILON      0.125                           /* the dead-zone befo
#define MOVE_FACTOR     2.5                             /* a movement scale f    #define MOVE_FACTOR     2.5                             /* a movement scale f
static  vrTime          last_time = -1.0;               /* initially set to a    static  vrTime          last_time = -1.0;               /* initially set to a
        vrTime          sim_time = vrCurrentSimTime();  /* the current time o            vrTime          sim_time = vrCurrentSimTime();  /* the current time o
        double          delta_time;                     /* time since last up            double          delta_time;                     /* time since last up
        double          joy_x, joy_y;                   /* Joystick values */            double          joy_x, joy_y;                   /* Joystick values */
 
        /*****************************************************/                          /*****************************************************/
        /** Determine delta time from last simulation frame **/                          /** Determine delta time from last simulation frame **/
        /*****************************************************/                          /*****************************************************/
        if (last_time == -1.0)                                                           if (last_time == -1.0)
                delta_time = 0.0;                                                                delta_time = 0.0;
        else    delta_time = sim_time - last_time;                                       else    delta_time = sim_time - last_time;
        last_time = sim_time;                           /* now that delta_tim            last_time = sim_time;                           /* now that delta_tim
 
        /* skip the update if the delta isn't big enough */                              /* skip the update if the delta isn't big enough */
        if (delta_time <= 0.0)  /* can also choose a non-zero epsilon */                 if (delta_time <= 0.0)  /* can also choose a non-zero epsilon */
                return;                                                                          return;
 
        /****************************************/                                       /****************************************/
        /* Move the objects with constant speed */                                       /* Move the objects with constant speed */
 
        /* object 1 travels in a horizontal circle at a fixed speed */                   /* object 1 travels in a horizontal circle at a fixed speed */
        wd->obj1_x = 3*sin(sim_time * 2.0);                                              wd->obj1_x = 3*sin(sim_time * 2.0);
        wd->obj1_z = 3*cos(sim_time * 2.0) - 5.0;                                        wd->obj1_z = 3*cos(sim_time * 2.0) - 5.0;
 
        /* object 2 travels in a verticle circle at a fixed speed */                     /* object 2 travels in a verticle circle at a fixed speed */
        wd->obj2_x = 3*cos(sim_time * 2.0);                                              wd->obj2_x = 3*cos(sim_time * 2.0);
        wd->obj2_y = 3*sin(sim_time * 2.0) + 5.0;                                        wd->obj2_y = 3*sin(sim_time * 2.0) + 5.0;
 
        /**************************/                                                     /**************************/
        /* User interface section */                                                     /* User interface section */
 
        /* the first button moves the object up, when initially pressed */               /* the first button moves the object up, when initially pressed */
        if (vrGet2switchDelta(1) == 1) {                                                 if (vrGet2switchDelta(1) == 1) {
                wd->obj3_y += 1.0;                                                               wd->obj3_y += 1.0;
        }                                                                                }
 
        /* the second button resets the object's location, when initially pre            /* the second button resets the object's location, when initially pre
        if (vrGet2switchDelta(2) == 1) {                                                 if (vrGet2switchDelta(2) == 1) {
                wd->obj3_x =  0.0;                                                               wd->obj3_x =  0.0;
                wd->obj3_y =  5.0;                                                               wd->obj3_y =  5.0;
                wd->obj3_z = -5.0;                                                               wd->obj3_z = -5.0;
        }                                                                                }
 
        /* the third button moves the object down, when initially pressed */             /* the third button moves the object down, when initially pressed */
        if (vrGet2switchDelta(3) == 1) {                                                 if (vrGet2switchDelta(3) == 1) {
                wd->obj3_y -= 1.0;                                                               wd->obj3_y -= 1.0;
        }                                                                                }
 
        /* store input values so they are consistent throughout update routin            /* store input values so they are consistent throughout update routin
        joy_x = vrGetValuatorValue(0);                                                   joy_x = vrGetValuatorValue(0);
        joy_y = vrGetValuatorValue(1);                                                   joy_y = vrGetValuatorValue(1);
 
        /* now use those values to move the object */                                    /* now use those values to move the object */
        if (fabs(joy_x) > JS_EPSILON)                                                    if (fabs(joy_x) > JS_EPSILON)
                wd->obj3_x += joy_x * delta_time * MOVE_FACTOR;                                  wd->obj3_x += joy_x * delta_time * MOVE_FACTOR;         
 
        if (fabs(joy_y) > JS_EPSILON)                                                    if (fabs(joy_y) > JS_EPSILON)
                wd->obj3_z -= (joy_y-copysign(JS_EPSILON,joy_y)) * delta_time                    wd->obj3_z -= (joy_y-copysign(JS_EPSILON,joy_y)) * delta_time
 
        /****************************************************/                           /****************************************************/
        /* yield to allow other processes access to the CPU */                           /* yield to allow other processes access to the CPU */
        vrSleep(0);                                                                      vrSleep(0);
}                                                                                }
 
 
 
/* ----------------8<-----------------8<-----------------8<----------------*/    /* ----------------8<-----------------8<-----------------8<----------------*/
/* In a non-example applicatation, the following would be a separate file. */    /* In a non-example applicatation, the following would be a separate file. */
 
                /**************************/                                                     /**************************/
                /**************************/                                                     /**************************/
                /** The Graphics section **/                                                     /** The Graphics section **/
                /**************************/                                                     /**************************/
                /**************************/                                                     /**************************/
 
/* functions from shapes.c */                                                    /* functions from shapes.c */
void    draw_cube();                                                             void    draw_cube();
void    draw_pyramid();                                                          void    draw_pyramid();
 
 
/*********************************************************************/          /*********************************************************************/
/* init_gfx(): initialize general graphics properties (eg. lighting) */          /* init_gfx(): initialize general graphics properties (eg. lighting) */
/*********************************************************************/          /*********************************************************************/
void init_gfx(void)                                                              void init_gfx(void)
{                                                                                {
        GLfloat location_0[4] = { 0.0, 10.0, 10.0, 1.0 /* 0 = directional, 1             GLfloat location_0[4] = { 0.0, 10.0, 10.0, 1.0 /* 0 = directional, 1 
                ambient_0[4] = { 0.2, 0.2, 0.2, 1.0 },                                           ambient_0[4] = { 0.2, 0.2, 0.2, 1.0 },
                diffuse_0[4] = { 0.8, 0.8, 0.8, 1.0 },                                           diffuse_0[4] = { 0.8, 0.8, 0.8, 1.0 },
                specular_0[4] = { 0.2, 0.2, 0.2, 1.0 };                                          specular_0[4] = { 0.2, 0.2, 0.2, 1.0 };
 
        /****************************************/                                       /****************************************/
        /* set the render initialization values */                                       /* set the render initialization values */
        glClearColor(0.0, 0.0, 0.0, 0.0);                                                glClearColor(0.0, 0.0, 0.0, 0.0);
        glClearDepth(1.0);                                                               glClearDepth(1.0);
 
        /********************************************************/                       /********************************************************/
        /* set the polygon shading/lighting/material parameters */                       /* set the polygon shading/lighting/material parameters */
        glShadeModel(GL_SMOOTH);                                                         glShadeModel(GL_SMOOTH);
 
        glEnable(GL_LIGHTING);                                                           glEnable(GL_LIGHTING);
 
        glLightfv(GL_LIGHT0, GL_POSITION, location_0);                                   glLightfv(GL_LIGHT0, GL_POSITION, location_0);
        glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_0);                                     glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_0);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_0);                                     glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_0);
        glLightfv(GL_LIGHT0, GL_SPECULAR, specular_0);                                   glLightfv(GL_LIGHT0, GL_SPECULAR, specular_0);
        glEnable(GL_LIGHT0);                                                             glEnable(GL_LIGHT0);
 
        /* These two lines cause the vertex colors to be used (otherwise all             /* These two lines cause the vertex colors to be used (otherwise all 
        glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);                               glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
        glEnable(GL_COLOR_MATERIAL);                                                     glEnable(GL_COLOR_MATERIAL);
 
        glEnable(GL_NORMALIZE); /* this is required when calling glScalef() w            glEnable(GL_NORMALIZE); /* this is required when calling glScalef() w
}                                                                                }
 
 
/*************************************************************/                  /*************************************************************/
/* draw_world(): Render the three objects in the world.      */                  /* draw_world(): Render the three objects in the world.      */
/*   Note that two of the objects (the blue cube, and red    */                  /*   Note that two of the objects (the blue cube, and red    */
/*   pyramid) get their locations from the world database    */                  /*   pyramid) get their locations from the world database    */
/*************************************************************/                  /*************************************************************/
void draw_world(WorldDataType *wd)                                               void draw_world(WorldDataType *wd, vrRenderInfo *rendinfo)
{                                                                                {
static  GLfloat         location_0[4] = { 0.0, 10.0, 0.0, 1.0 /* 0 = directio    static  GLfloat         location_0[4] = { 0.0, 10.0, 0.0, 1.0 /* 0 = directio
                                                                                 static  char            msg[128];                       /* memory for text st
 
        /**************************************/                                         /**************************************/
        /* clear the screen -- very important */                                         /* clear the screen -- very important */
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                              glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
        /********************************************************************            /********************************************************************
        /* update the light's location -- must be done independently for each            /* update the light's location -- must be done independently for each
        glLightfv(GL_LIGHT0, GL_POSITION, location_0);                                   glLightfv(GL_LIGHT0, GL_POSITION, location_0);
 
        /************************/                                                       /************************/
        /* draw all the objects */                                                       /* draw all the objects */
 
        /* a blue cube */                                                                /* a blue cube */
        glColor3ub(100, 100, 255);                                                       glColor3ub(100, 100, 255);
        glPushMatrix();                                                                  glPushMatrix();
                glTranslatef(wd->obj1_x, wd->obj1_y, wd->obj1_z);                                glTranslatef(wd->obj1_x, wd->obj1_y, wd->obj1_z);
                draw_cube();                                                                     draw_cube();
        glPopMatrix();                                                                   glPopMatrix();
 
        /* a red pyramid */                                                              /* a red pyramid */
        glColor3ub(255, 100, 100);                                                       glColor3ub(255, 100, 100);
        glPushMatrix();                                                                  glPushMatrix();
                glTranslatef(wd->obj2_x, wd->obj2_y, wd->obj2_z);                                glTranslatef(wd->obj2_x, wd->obj2_y, wd->obj2_z);
                draw_pyramid();                                                                  draw_pyramid();
        glPopMatrix();                                                                   glPopMatrix();
 
        /* the green upside-down pyramid */                                              /* the green upside-down pyramid */
        glColor3ub(100, 255, 100);                                                       glColor3ub(100, 255, 100);
        glTranslatef(wd->obj3_x, wd->obj3_y, wd->obj3_z);                                glPushMatrix();
        glScalef(1.0, -1.0, 1.0);                                                                glTranslatef(wd->obj3_x, wd->obj3_y, wd->obj3_z);
        draw_pyramid();                                                                          glScalef(1.0, -1.0, 1.0);
                                                                                                 draw_pyramid();
                                                                                         glPopMatrix();
                                                                                
                                                                                         /*************************************************/
                                                                                         /* draw text indicating the location of object 3 */
                                                                                         sprintf(msg, "Green pyramid is at %.2f %.2f %.2f", wd->obj3_x, wd->ob
                                                                                
                                                                                         glDisable(GL_LIGHTING);         /* make the text bright regardless of
                                                                                         glColor3ub(255, 255, 255);      /* white */
                                                                                         glPushMatrix();
                                                                                                 glTranslatef(-4.0, 7.0, -5.0);  /* on the front screen */
                                                                                
                                                                                                 glRasterPos3f(0.0, 0.0, 0.0);   /* go to the 3D location */
                                                                                                 vrRenderText(rendinfo, msg);
                                                                                         glPopMatrix();
                                                                                         glEnable(GL_LIGHTING);
}                                                                                }