home *** CD-ROM | disk | FTP | other *** search
/ ftp.hitl.washington.edu / ftp.hitl.washington.edu.tar / ftp.hitl.washington.edu / pub / people / peter / ER / mmvr_demo.cxx < prev    next >
C/C++ Source or Header  |  1998-07-07  |  65KB  |  2,082 lines

  1. /*
  2.  * mmvr_demo.cc -- Laparoscopy in the virtual ER
  3.  *
  4.  */
  5.  
  6. //#define IR_DEMO    // Enables extra features;
  7.             // use if running the demo on fast machine
  8.             // or a machine with lots of texture memory.
  9. //#define O2_DEMO    // Uses monocular viewer.
  10. //#define VIDEO_DEMO    // Include a live video window.
  11. #define SAVE_FRAME_RATE    // Cuts out a few extra objects to squeeze more frames.
  12.  
  13. #include <iostream.h>
  14. #include <ctype.h>
  15.  
  16. #include <list.h>
  17. #include <vector.h>
  18.  
  19. #include "mmvr_demo.h"
  20.  
  21. #include "v3dSystem.h"
  22. #include "v3dUniverse.h"
  23. #include "v3dViewpoint.h"
  24. #include "v3dColor.h"
  25. #include "v3dCollide.h"
  26. #include "v3dLight.h"
  27. #include "v3dKeyboard.h"
  28. #include "v3dMouse.h"
  29. #include "v3dMaterial.h"
  30. #include "v3dButtonBox.h"
  31.  
  32. #include "ECG_driver.h"
  33. #include "ECG_plot.h"
  34. #include "ECG_heart_model.h"
  35.  
  36. #include "BP_driver.h"
  37. #include "BP_plot.h"
  38.  
  39. #include "ImageSequenceViewer.h"
  40. #include "ThickObject.h"
  41. #include "SoundClient.h"
  42. #include "ObjectSelectionTracker.h"
  43. #include "PivotTracker.h"
  44. #include "CmdLine.h"
  45.  
  46. #include "x3dITGoGo.h"
  47.  
  48. #ifdef VIDEO_DEMO
  49. #include "VideoObject.h"
  50. #endif    // VIDEO_DEMO
  51.  
  52. ////////////////////////////////////////////////////////////////////////////////
  53. // Global objects in the simulation
  54. ////////////////////////////////////////////////////////////////////////////////
  55.  
  56. v3dUnit* Universe_Unit;
  57.  
  58. ImageSequenceViewer *User_1_Rep = 0;    // Representations of user 1 and 2.
  59. #ifdef VIDEO_DEMO
  60. VideoObject *User_2_Rep = 0;
  61. #else
  62. ImageSequenceViewer *User_2_Rep = 0;
  63. #endif    // VIDEO_DEMO
  64.  
  65. ImageSequenceViewer *MRI_Viewer = 0;    // Head MRI.
  66. const int NUM_MRI_SERIES = 6;
  67. int Num_MRI_Textures_Per_Series[8] = { 11, 26, 124, 28, 24, 28 };
  68. int Current_MRI_Series = -1;
  69.  
  70. ObjectSelectionTracker *Selection_Tracker;
  71.  
  72. // Variables related to the user's hand.
  73. v3dActionPoint  *Hand_1_AP, *Hand_2_AP;
  74. bool Use_2_Hands = false;
  75. x3dInteractionTechnique    *Current_IT_1 = 0, *Current_IT_2 = 0;
  76.  
  77. // Position and direction of the light in the room.
  78. const v3dPos LIGHT_POS(1200, -800, -1000);
  79. const v3dDir LIGHT_DIR( -.68, .48, .58);
  80.  
  81. // Starting positions and sizes of various objects in the world.
  82. const v3dPos    USER_2_START_POS (1.145, 0.828, -0.069);
  83.  
  84. v3dObject *Patient;
  85. v3dObject *Inner_Abd;
  86.  
  87. v3dObject *Cyl1, *Cyl2, *Cyl3, *Cyl4, *HV_Floor;
  88. #ifdef IR_DEMO
  89. v3dObject *Mash_Floor;
  90. v3dObject *Outside_Cyl1, *Outside_Cyl2, *Outside_Cyl3, *Outside_Cyl4,
  91.     *Outside_Floor;
  92. v3dTexture *Outside_Tex_1, *Outside_Tex_2, *Outside_Tex_3, *Outside_Tex_4;
  93. #endif    // IR_DEMO;
  94. v3dTexture *HV_1, *HV_2, *HV_3, *HV_4;
  95.  
  96. SoundClient *Sound_Client = 0;
  97.  
  98. ////////////////////////////////////////////////////////////////////////////////
  99. // Structures for storing object information
  100. ////////////////////////////////////////////////////////////////////////////////
  101.  
  102. typedef struct {
  103.     v3dObject*    object;
  104.     int        object_ID;
  105.     int        object_type_ID;
  106.     bool        is_interactive;
  107.     v3dPos        original_position;
  108.     v3dOrient    original_orientation;
  109. } ObjectInfo;
  110.  
  111. typedef map<int, ObjectInfo*, less<int> > ObjectMap;
  112.  
  113. ObjectMap Objects;    // The list of all objects in the simulation.
  114. ObjectMap Interactive_Objects;
  115.             // The list of all interactive objects in the sim.
  116.  
  117. ////////////////////////////////////////////////////////////////////////////////
  118. // Window-related variables
  119. ////////////////////////////////////////////////////////////////////////////////
  120.  
  121. // The WTK windows to be used in the simulation.
  122. WTwindow *WRight, *WLeft;
  123. WTwindow *WLeft_Endo, *WRight_Endo;
  124.  
  125. // The viewpoint for the users.
  126. v3dViewPoint *User_1_View, *User_2_View;
  127.  
  128. // The viewpoint for the endoscopic view.
  129. v3dViewPoint *Endo_View;
  130.  
  131. // Window dimensions
  132. const long WINDOW_WIDTH = 640;
  133. const long WINDOW_HEIGHT = 480;
  134. const long WINDOW_2_LEFT_POS = 642;
  135.  
  136. // Parameters for the endoscopic view window.
  137. const int ENDO_VIEW_WIDTH = 200;
  138. const int ENDO_VIEW_HEIGHT = 150;
  139. const int ENDO_VIEW_BORDER = 10;
  140. const int ENDO_WINDOW_MAX_POS = 3;
  141. int Endo_Window_Current_Pos = 0;
  142. long Endo_View_X[ENDO_WINDOW_MAX_POS] =
  143.             { WINDOW_WIDTH - ENDO_VIEW_BORDER - 2 * ENDO_VIEW_WIDTH,
  144.             2 * ENDO_VIEW_BORDER,
  145.             WINDOW_WIDTH - ENDO_VIEW_BORDER - 2 * ENDO_VIEW_WIDTH };
  146. //            WINDOW_WIDTH + ENDO_VIEW_BORDER };
  147. long Endo_View_Y[ENDO_WINDOW_MAX_POS] =
  148.             { ENDO_VIEW_BORDER,
  149.             2 * ENDO_VIEW_BORDER,
  150.             WINDOW_HEIGHT + ENDO_VIEW_BORDER };
  151. //            WINDOW_HEIGHT - ENDO_VIEW_BORDER - ENDO_VIEW_HEIGHT }
  152. const float ENDO_VIEW_ANGLE = 30 * PI / 180.0;
  153. float Normal_View_Angle;
  154. bool Show_User_2 = false, Show_Endo_View = true;
  155.  
  156. //////////////////////////////////////////////////////////////////////////////
  157. // Global variables for the cauterization task.
  158. //////////////////////////////////////////////////////////////////////////////
  159. // The list of cautery objects.
  160. //const int CAUTERY_NUM = 19;
  161. const int CAUTERY_NUM = 15;
  162.     // The number of cauterization objects to load.
  163. const int CAUTERY_TEXTURE_INDEX_START = 2;
  164.     // The index of the first cauterization object to load.
  165. const int CAUTERY_TEXTURE_FLIP_NUM = 9;
  166.     // The index of the cauterization past which point all textures
  167.     // should be flipped.
  168. int Cauterized_Count = 0;
  169.     // The number of cauterization objects the user has successfully
  170.     // cauterized.
  171.  
  172. typedef vector<v3dObject *> CauteryList;
  173. CauteryList Cautery_Objects;
  174.     // The array of cautery objects.
  175.  
  176. typedef vector<bool> CauteryStatusList;
  177. CauteryStatusList Cautery_Status;
  178.     // An array of cautery status flags.
  179.     //    true = cauterized
  180.     //    false = not cauterized
  181.  
  182. v3dCustomObject *Gall_Bladder;
  183. v3dPos Gall_Bladder_Initial_Pos;
  184. v3dOrient Gall_Bladder_Initial_Orient;
  185.     // The object that represents the gall bladder and its initial position
  186.     // and orientation.  The gall bladder is hidden until the user
  187.     // successfully completes the cauterization task.  After it is shown,
  188.     // the gall bladder can be picked up with the forceps.
  189.  
  190. v3dCustomObject *Under_Gall_Bladder;
  191.     // The object to be displayed under the gall bladder after the
  192.     // cauterization task has been successfully completed.
  193.  
  194. bool Gall_Bladder_Picked = false;
  195.     // True if the user has the gall bladder in the forceps,
  196.     // false otherwise.
  197.  
  198. PivotObject *Endoscope, *Forceps, *Electrode;
  199.     // The laparoscopic instruments in the simulation.
  200. PivotTracker *Pivot_Tracker;
  201.     // The class responsible for handling manipulation of the laparoscopic
  202.     // instruments.
  203.  
  204. const v3dPos FORCEPS_INITIAL_POS (0.06, 0.7, -0.21);
  205. const v3dPos ELECTRODE_INITIAL_POS (0.06, 0.75, 0.21);
  206. const v3dPos ENDOSCOPE_INITIAL_POS (0.1, 0.75, 0);
  207.     // The initial positions of the instruments.
  208.  
  209. //////////////////////////////////////////////////////////////////////////////
  210. // Function prototypes.
  211. //////////////////////////////////////////////////////////////////////////////
  212. // Function prototypes for event handlers.
  213. v3dEventHandlerCallback key_press_handler, mouse_press_handler,
  214.     pivot_object_collide, pivot_object_collide_2_hands,
  215.     button_box_press_handler, button_box_down_handler,
  216.     object_select_handler, object_pick_handler, object_unpick_handler,
  217.     object_set_pivot_handler, object_unset_pivot_handler;
  218.  
  219. // Prototypes for cauterization-relation functions.
  220. void cauterize();
  221. void toggle_jaws(v3dActionPoint *hand_AP);
  222.  
  223. void move_hand_to_camera();
  224. void correct_viewpoint(WTobject *obj);
  225. void correct_viewpoint_CB(const v3dEventMessage *message);
  226. float sens_coef(float v_l, float g_l, float res);
  227.  
  228. void calculate_extents (v3dObject *object,
  229.     v3dPos &min_point, v3dPos &max_point);
  230.  
  231. void user_2_vp_move_CB(const v3dEventMessage *message);
  232. void endo_vp_move_CB(const v3dEventMessage *message);
  233. bool load_MRI_series(int new_series_number);
  234.  
  235. void create_room();
  236. void create_patient();
  237.  
  238. bool load_configuration_file(char *config_filename);
  239. bool skip_comment(FILE *infile);
  240. v3dObject* get_object_by_ID(int object_ID);
  241. ECGDriver* get_ECG_driver(FILE* fp, int ECG_driver_type);
  242.  
  243. ////////////////////////////////////////////////////////////////////////////////
  244. // main
  245. ////////////////////////////////////////////////////////////////////////////////
  246.  
  247. main(int argc, char *argv[])
  248. {
  249.  
  250.     // Parse the command line options.
  251.     CmdLine *cl = CmdLine::getInstanceOf();
  252.     cl->makeCaseInsensitive();
  253.         cl->addArgument("config", "config.data",
  254.         "Specify target data configuration file");
  255.     cl->addArgument("serial", 2, "Serial port for Fastrak");
  256.     cl->addArgument("baud", 19200, "Baud rate for Fastrak");
  257.     cl->addArgument("buttons", "Use Chris Shaw's Polhemus button box");
  258.     cl->addArgument("stereo", "Run world in stereo view");
  259.     cl->addArgument("twousers", "Show second user view");
  260.     cl->addArgument("noendo", "Don't show endoscopic view");
  261.     cl->addArgument("twohands", "Use two hands");
  262.     cl->addArgument("usesound", "Add sound effects to the simulation");
  263.     cl->addArgument("soundhost", "siksik",
  264.         "Host computer of the sound server");
  265.     cl->addArgument("soundport", 2500,
  266.         "Socket port to use for the sound server");
  267.  
  268.     if (!(cl->parse(argc, argv)))
  269.         return;
  270.  
  271.     int serial_port_int = cl->queryInt("serial");
  272.     v3dSerial serial_port, button_serial_port;
  273.     if (serial_port_int == 2) {
  274.         serial_port = Serial_2;
  275.         button_serial_port = Serial_1;
  276.     } else if (serial_port_int == 1) {
  277.         serial_port = Serial_1;
  278.         button_serial_port = Serial_2;
  279.     } else {
  280.         fprintf (stderr, "*** Bad serial port: %d\n*** Using port 2\n",
  281.             serial_port_int);
  282.         serial_port = Serial_2;
  283.         button_serial_port = Serial_1;
  284.     }
  285.  
  286.     int fastrak_baud = cl->queryInt("baud");
  287.  
  288.     Use_2_Hands = cl->queryFlag("twohands");
  289.     Show_User_2 = cl->queryFlag("twousers");
  290.     Show_Endo_View = !cl->queryFlag("noendo");
  291.     bool use_button_box = cl->queryFlag("buttons"),
  292.         use_stereo = cl->queryFlag("stereo"),
  293.         use_sound = cl->queryFlag("usesound");
  294.  
  295.         // Get the file name of the scenario configuration file.
  296.         char *config_file = cl->queryString("config");
  297.  
  298.     // Initialize the sound client, if requested.
  299.     char *sound_host = cl->queryString("soundhost");
  300.     int sound_port = cl->queryInt("soundport");
  301.     if (use_sound) {
  302.         Sound_Client = new SoundClient();
  303.         fprintf (stderr, "Opening sound client to host %s port %d\n",
  304.             sound_host, sound_port);
  305.         if (!Sound_Client->OpenSocket(sound_host, sound_port)) {
  306.             fprintf (stderr, "Unable to open sound client\n");
  307.             delete Sound_Client;
  308.             Sound_Client = 0;
  309.         }
  310.     }
  311.  
  312.  
  313.     cout << "Initializing the system ...\n";
  314.     v3dSystem *system;
  315.     if (use_stereo)
  316.         system = v3dSystem::StartUp(v3dSystem::Stereo_2_Windows);
  317.     else
  318.         system = v3dSystem::StartUp(v3dSystem::Mono);
  319.  
  320.     // Set initial background color to black
  321.     v3dUniverse *universe = system->GetCurrentUniverse();
  322.     universe->SetBgColor(v3dColor(0, 0, 0));
  323.  
  324.     // Set ambient light 
  325.     universe->SetAmbient(0.5);
  326.  
  327.     cout << "Loading lights ... \n";
  328.     v3dLight light (LIGHT_POS, LIGHT_DIR, 1.0);
  329.  
  330.     // Create the simulation room.
  331.     create_room();
  332.  
  333.     // Set units to meters. We will consider the room to be 5 meters long.
  334.     Universe_Unit = new v3dUnit(Meter, universe->GetDimensions().max()/4.5);
  335.     v3dUnit &unit = *Universe_Unit;
  336. ;
  337.  
  338.     // Create the patient.
  339.     create_patient();
  340.  
  341.     // Add the endoscopic tools.
  342.     v3dPos center_pos = Inner_Abd->GetAbsolutePosition();
  343.     float center_obj_radius = Inner_Abd->GetSize().max() / 2.0;
  344.     Inner_Abd->Hide();
  345.     Forceps = new PivotObject(center_pos, center_obj_radius,
  346.         "Laparoscopy/forceps10.tube.obj",
  347.         "Laparoscopy/forceps10.handle.obj",
  348.         "Laparoscopy/forceps9.jaws.open.obj",
  349.         "Laparoscopy/forceps9.jaws.closed.obj");
  350.     Electrode = new PivotObject(center_pos, center_obj_radius,
  351.         "Laparoscopy/forceps10.tube.obj",
  352.         "Laparoscopy/forceps10.handle.obj",
  353.         "Laparoscopy/forceps9.cauterhead.obj");
  354.     Endoscope = new PivotObject(center_pos, center_obj_radius,
  355.         "Laparoscopy/endoscope2.tube.obj",
  356.         "Laparoscopy/endoscope2.head.obj");
  357.  
  358.     v3dDim dim = Forceps->GetSize();
  359.     float factor = 0.08/unit.ConvToUnits(dim.max());
  360.     Forceps->Scale(factor);
  361.     Forceps->UpdatePosition(unit.ConvToPoints(FORCEPS_INITIAL_POS),
  362.         v3dOrient());
  363.     dim = Endoscope->GetSize();
  364.     factor = 0.08/unit.ConvToUnits(dim.max());
  365.     Endoscope->Scale(factor);
  366.     Endoscope->SetColor(v3dColor(160, 160, 190));
  367.     Endoscope->UpdatePosition(unit.ConvToPoints(ENDOSCOPE_INITIAL_POS),
  368.         v3dOrient());
  369.     dim = Electrode->GetSize();
  370.     factor = 0.08/unit.ConvToUnits(dim.max());
  371.     Electrode->Scale(factor);
  372.     Electrode->UpdatePosition(unit.ConvToPoints(ELECTRODE_INITIAL_POS),
  373.         v3dOrient());
  374.  
  375.     // Read in the scenario configuration file.
  376.     if (!load_configuration_file(config_file)) {
  377.                 fprintf(stderr, "*** Error loading configuration file\n***Exiting\n");
  378.         exit(1);
  379.     }
  380.  
  381.     // Set up keyboard and mouse
  382.     v3dMouse mouse;
  383.     v3dAutoEventReceiver mouse_press_receiver (&mouse,
  384.                         v3dMouse::MOUSE_BUTTON_PRESS,
  385.                         &mouse_press_handler, 0);
  386.     mouse.SetSensitivity(0.005 * universe->GetRadius());
  387.  
  388.     v3dKeyboard keyboard;
  389.     v3dAutoEventReceiver keyboard_receiver (&keyboard,
  390.                         v3dKeyboard::KEYBOARD_KEY_PRESS,
  391.                         &key_press_handler, 0);
  392.  
  393.     // Set up button box.
  394.     v3dButtonBox *button_box;
  395.     v3dAutoEventReceiver *button_press_receiver, *button_down_receiver;
  396.     if (use_button_box) {
  397.         button_box = new v3dButtonBox(button_serial_port);
  398.         button_press_receiver = new v3dAutoEventReceiver(button_box,
  399.                 v3dButtonBox::BUTTON_BOX_BUTTON_PRESS,
  400.                 &button_box_press_handler, 0);
  401.         button_down_receiver = new v3dAutoEventReceiver(button_box,
  402.                 v3dButtonBox::BUTTON_BOX_BUTTON_DOWN,
  403.                 &button_box_down_handler, 0);
  404.     }
  405.  
  406.     // Open the Polhemus Fastrak sensors.
  407.     v3dFastrak *view_sensor = new v3dFastrak(serial_port, 1, fastrak_baud);
  408.     v3dFastrak *hand_1_sensor = new v3dFastrak (serial_port, 2, fastrak_baud);
  409.     // Set up second hand tracker, if requested.
  410.     v3dFastrak    *hand_2_sensor;
  411.     if (Use_2_Hands)
  412.         hand_2_sensor = new v3dFastrak (serial_port, 3, fastrak_baud);
  413.  
  414.     // Get viewpoint from the Universe
  415.     User_1_View = universe->GetCurrentViewPoint();
  416.  
  417.     User_1_View->AttachSensor (view_sensor);
  418.  
  419.     float height = (Patient->GetSize()).max();
  420.     // CHANGE: Replace constants with numbers based on scale.
  421.     view_sensor->SetSensitivity (sens_coef(1.8, height, 0.015));
  422.     hand_1_sensor->SetSensitivity(sens_coef(1.8, height, 0.015));
  423.     if (Use_2_Hands)
  424.         hand_2_sensor->SetSensitivity(sens_coef(1.8, height, 0.015));
  425.  
  426.     // Set parallax
  427.     User_1_View->SetParallax(2.6437);
  428.  
  429.     User_1_View->Translate (unit.ConvToPoints(v3dPos (-.1, .5, -.2)),
  430.         Frame_World);
  431.  
  432.     // Add representations of the two users to the scene.
  433.     v3dCustomObject *tom_head = new v3dCustomObject("tom1.nff");
  434.     User_1_Rep = new ImageSequenceViewer(tom_head, 1, false);
  435.     User_1_Rep->LoadNewSequence("ivan", ".rgb", 1, 1, false);
  436.     dim = User_1_Rep->GetSize();
  437.     factor = 0.5/unit.ConvToUnits(dim.max());
  438.     User_1_Rep->MoveAbsolute(User_1_View->GetAbsolutePosition());
  439.     User_1_Rep->SetAbsoluteOrientation(User_1_View->GetAbsoluteOrientation());
  440.     User_1_Rep->Scale(factor);
  441.     User_1_Rep->AttachSensor(view_sensor);
  442.  
  443. #ifdef VIDEO_DEMO
  444.  
  445.     User_2_Rep = VideoObject::GetInstance();
  446.     dim = User_2_Rep->GetSize();
  447. cerr << "Video size: " << dim << endl;
  448.     User_2_Rep->SetOrientation(v3dOrient(v3dDir(1,0,0)));
  449.  
  450. cerr << "Video start pos: " << unit.ConvToPoints(USER_2_START_POS) << endl;
  451. cerr << "Video size: " << User_2_Rep->GetSize() << " Video scale factor: " << factor << endl;
  452.  
  453. #else    // VIDEO_DEMO
  454.  
  455.     User_2_Rep = new ImageSequenceViewer(tom_head, 1, false);
  456.     User_2_Rep->LoadNewSequence("tom", ".rgb", 1, 2, false);
  457.     User_2_Rep->SetOrientation(v3dOrient(v3dDir(-1,0,0)));
  458.  
  459. #endif    // VIDEO_DEMO
  460.  
  461.     dim = User_2_Rep->GetSize();
  462.     factor = 0.5/unit.ConvToUnits(dim.max());
  463.     User_2_Rep->Move(unit.ConvToPoints(USER_2_START_POS));
  464.     User_2_Rep->Scale(factor);
  465.  
  466.     RestoreInfo::Add(User_2_Rep);
  467.  
  468.     delete tom_head;
  469.  
  470.     // Create the user's hands.
  471.     v3dCustomObject *hand_1_object = new v3dCustomObject("hand2.nff");
  472.     dim = hand_1_object->GetSize(); // get size of the hand in points
  473.     factor = 0.125/unit.ConvToUnits(dim.max());
  474.     hand_1_object->Scale(factor);
  475.     hand_1_object->MoveToOrigin();
  476.  
  477.     v3dCustomObject *hand_2_object;
  478.     if (Use_2_Hands) {
  479.         hand_2_object = new v3dCustomObject("hand2.nff");
  480.         hand_2_object->Scale(factor);
  481.         hand_2_object->MoveToOrigin();
  482.     }
  483.  
  484.     Hand_1_AP = new v3dActionPoint (hand_1_sensor, hand_1_object);
  485.     if (Use_2_Hands) {
  486.         Hand_2_AP = new v3dActionPoint (hand_2_sensor, hand_2_object);
  487.     }
  488.     move_hand_to_camera();
  489.  
  490.     // Register the interactive objects in the simulation with the
  491.     // selection tracker.
  492.     Selection_Tracker = new ObjectSelectionTracker();
  493.     Selection_Tracker->RegisterActionPoint(Hand_1_AP);
  494.     Selection_Tracker->RegisterObject(User_2_Rep);
  495.  
  496.     for (ObjectMap::iterator it = Interactive_Objects.begin();
  497.             it != Interactive_Objects.end(); ++it)
  498.         Selection_Tracker->RegisterObject((*it).second->object);
  499.  
  500.     // Initialize the pivot object tracker.
  501.     Pivot_Tracker = new PivotTracker;
  502.     Pivot_Tracker->RegisterActionPoint(Hand_1_AP);
  503.     Pivot_Tracker->RegisterObject(Endoscope);
  504.     Pivot_Tracker->RegisterObject(Forceps);
  505.     Pivot_Tracker->RegisterObject(Electrode);
  506.  
  507.     v3dAutoEventReceiver *object_select_receiver, *object_pick_receiver,
  508.         *object_unpick_receiver, *pivot_object_select_receiver,
  509.         *pivot_object_pick_receiver, *pivot_object_unpick_receiver,
  510.         *pivot_object_set_pivot_receiver,
  511.         *pivot_object_unset_pivot_receiver;
  512.     if (Sound_Client) {
  513.         object_select_receiver = new v3dAutoEventReceiver(
  514.                 Selection_Tracker,
  515.                 ObjectSelectionTracker::OBJECT_SELECT,
  516.                 &object_select_handler, 0);
  517.         object_pick_receiver = new v3dAutoEventReceiver(
  518.                 Selection_Tracker,
  519.                 ObjectSelectionTracker::OBJECT_PICK,
  520.                 &object_pick_handler, 0);
  521.         object_unpick_receiver = new v3dAutoEventReceiver(
  522.                 Selection_Tracker,
  523.                 ObjectSelectionTracker::OBJECT_UNPICK,
  524.                 &object_unpick_handler, 0);
  525.  
  526.         pivot_object_select_receiver = new v3dAutoEventReceiver(
  527.                 Pivot_Tracker,
  528.                 PivotTracker::OBJECT_SELECT,
  529.                 &object_select_handler, 0);
  530.         pivot_object_pick_receiver = new v3dAutoEventReceiver(
  531.                 Pivot_Tracker,
  532.                 PivotTracker::OBJECT_PICK,
  533.                 &object_pick_handler, 0);
  534.         pivot_object_unpick_receiver = new v3dAutoEventReceiver(
  535.                 Pivot_Tracker,
  536.                 PivotTracker::OBJECT_UNPICK,
  537.                 &object_unpick_handler, 0);
  538.  
  539.         pivot_object_set_pivot_receiver = new v3dAutoEventReceiver(
  540.                 Pivot_Tracker,
  541.                 PivotTracker::OBJECT_SET_PIVOT,
  542.                 &object_set_pivot_handler, 0);
  543.         pivot_object_unset_pivot_receiver = new v3dAutoEventReceiver(
  544.                 Pivot_Tracker,
  545.                 PivotTracker::OBJECT_UNSET_PIVOT,
  546.                 &object_unset_pivot_handler, 0);
  547.     }
  548.  
  549.     if (Use_2_Hands) {
  550.         Selection_Tracker->RegisterActionPoint(Hand_2_AP);
  551.         Pivot_Tracker->RegisterActionPoint(Hand_2_AP);
  552.     }
  553.  
  554.     // Hack to correct for offset of viewpoint sensor on headmount.
  555.     v3dAutoEventReceiver vp_correction_receiver (universe,
  556.         v3dUniverse::UNIVERSE_SIMULATION_LOOP, &correct_viewpoint_CB);
  557.     v3dObject* dummy = new v3dBlockObject(1,1,1);
  558.     WTobject_settask((WTobject *)dummy->GetHandler(), correct_viewpoint);
  559.     dummy->Hide();
  560.  
  561.     WLeft = WTuniverse_getwindows();
  562.     WTwindow_setposition(WLeft, 1, 1, WINDOW_WIDTH, WINDOW_HEIGHT);
  563.  
  564.     // Create the second user's viewpoint.
  565.     if (Show_User_2) {
  566.         WRight = WTwindow_new(WINDOW_2_LEFT_POS, 1, WINDOW_WIDTH,
  567.             WINDOW_HEIGHT, WTWINDOW_NOBORDER);
  568.         WTwindow_setrootnode(WRight, WTwindow_getrootnode(WLeft));
  569.         User_2_View = new v3dViewPoint();
  570.         WTwindow_setviewpoint(WRight,
  571.             (WTviewpoint *)User_2_View->GetImplement()->GetHandler());
  572.     }
  573.  
  574.     v3dAutoEventReceiver user_2_vp_move (universe,
  575.         Show_User_2 ? v3dUniverse::UNIVERSE_SIMULATION_LOOP : 100,
  576.         &user_2_vp_move_CB, 0);
  577.  
  578.     // Create the endoscopic views.
  579.     Endo_View = new v3dViewPoint();
  580.     Normal_View_Angle = WTwindow_getviewangle(WLeft);
  581.     if (Show_Endo_View) {
  582.         WLeft_Endo = WTwindow_new(
  583.             Endo_View_X[0], Endo_View_Y[0],
  584.             ENDO_VIEW_WIDTH, ENDO_VIEW_HEIGHT, WTWINDOW_NOBORDER);
  585.         WTwindow_setviewangle(WLeft_Endo, ENDO_VIEW_ANGLE);
  586.         WTwindow_setrootnode(WLeft_Endo, WTwindow_getrootnode(WLeft));
  587.         WTwindow_setviewpoint(WLeft_Endo,
  588.             (WTviewpoint *)Endo_View->GetImplement()->GetHandler());
  589.  
  590.         if (Show_User_2) {
  591.             WRight_Endo = WTwindow_new(
  592.                 WINDOW_2_LEFT_POS + WINDOW_WIDTH -
  593.                     ENDO_VIEW_BORDER - ENDO_VIEW_WIDTH,
  594.                 1 + WINDOW_HEIGHT - ENDO_VIEW_BORDER -
  595.                     ENDO_VIEW_HEIGHT,
  596.                 ENDO_VIEW_WIDTH, ENDO_VIEW_HEIGHT,
  597.                 WTWINDOW_NOBORDER);
  598.             WTwindow_setviewangle(WRight_Endo, ENDO_VIEW_ANGLE);
  599.             WTwindow_setrootnode(WRight_Endo,
  600.                 WTwindow_getrootnode(WLeft));
  601.             WTwindow_setviewpoint(WRight_Endo,
  602.                 (WTviewpoint *)Endo_View->GetImplement()->GetHandler());
  603.         }
  604.     }
  605.     v3dAutoEventReceiver endo_vp_move (universe,
  606.         v3dUniverse::UNIVERSE_SIMULATION_LOOP, &endo_vp_move_CB, 0);
  607.  
  608.     // Enter main loop 
  609.     cout << "Starting an application!\n";
  610.     system->Go();
  611.  
  612.     return 0;
  613. }
  614.  
  615. ////////////////////////////////////////////////////////////////////////////////
  616. // Create room
  617. ////////////////////////////////////////////////////////////////////////////////
  618. // This function creates the room used in the simulation by loading four
  619. // quarter-cylinders and covering them with ER textures.
  620. void
  621. create_room()
  622. {
  623.     // Create cylindrical walls of the emergency room and apply
  624.     // the ER texture maps to them.
  625.     Cyl1 = new v3dCustomObject("cyl1.obj");
  626.     HV_1 = new v3dTexture("pan_2.rgb");
  627.     HV_1->SetLinearXYWrapMode(1.);
  628.     HV_1->ApplyToObject(*Cyl1);
  629.  
  630.     Cyl2 = new v3dCustomObject("cyl2.obj");
  631.     HV_2 = new v3dTexture("pan_1.rgb");
  632.     HV_2->SetLinearXYWrapMode(1.);
  633.     HV_2->ApplyToObject(*Cyl2);
  634.  
  635.     Cyl3 = new v3dCustomObject("cyl3.obj");
  636.     HV_3 = new v3dTexture("pan_0.rgb");
  637.     HV_3->SetLinearXYWrapMode(1.);
  638.     HV_3->ApplyToObject(*Cyl3);
  639.  
  640.     Cyl4 = new v3dCustomObject("cyl4.obj");
  641.     HV_4 = new v3dTexture("pan_3.rgb");
  642.     HV_4->SetLinearXYWrapMode(1.);
  643.     HV_4->ApplyToObject(*Cyl4);
  644.  
  645.     HV_Floor = new v3dCustomObject("floor");
  646.  
  647. #ifdef IR_DEMO
  648.     Outside_Cyl1 = new v3dCustomObject("cyl1.obj");
  649.     Outside_Tex_1 = new v3dTexture("field.pan.2.rgb");
  650.     Outside_Tex_1->SetLinearXYWrapMode(1.);
  651.     Outside_Tex_1->ApplyToObject(*Outside_Cyl1);
  652.  
  653.     Outside_Cyl2 = new v3dCustomObject("cyl2.obj");
  654.     Outside_Tex_2 = new v3dTexture("field.pan.1.rgb");
  655.     Outside_Tex_2->SetLinearXYWrapMode(1.);
  656.     Outside_Tex_2->ApplyToObject(*Outside_Cyl2);
  657.  
  658.     Outside_Cyl3 = new v3dCustomObject("cyl3.obj");
  659.     Outside_Tex_3 = new v3dTexture("field.pan.0.rgb");
  660.     Outside_Tex_3->SetLinearXYWrapMode(1.);
  661.     Outside_Tex_3->ApplyToObject(*Outside_Cyl3);
  662.  
  663.     Outside_Cyl4 = new v3dCustomObject("cyl4.obj");
  664.     Outside_Tex_4 = new v3dTexture("field.pan.3.rgb");
  665.     Outside_Tex_4->SetLinearXYWrapMode(1.);
  666.     Outside_Tex_4->ApplyToObject(*Outside_Cyl4);
  667.  
  668.     Mash_Floor = new v3dCustomObject("mash_floor");
  669.     Mash_Floor->Hide();
  670.     Outside_Floor = new v3dCustomObject("outside_floor");
  671.     Outside_Floor->Hide();
  672.     Outside_Cyl1->Hide();
  673.     Outside_Cyl2->Hide();
  674.     Outside_Cyl3->Hide();
  675.     Outside_Cyl4->Hide();
  676. #endif    // IR_DEMO;
  677.  
  678.     // Calculate the extents of the cylindrical walls, find the center,
  679.     // then move that center to (0, 0, 0).
  680.     v3dPos obj_center = Cyl1->GetAbsolutePosition();
  681.     v3dDim obj_size = Cyl1->GetSize();
  682.     v3dPos min_point = obj_center - (obj_size / 2),
  683.         max_point = obj_center + (obj_size / 2);
  684.     calculate_extents (Cyl2, min_point, max_point);
  685.     calculate_extents (Cyl3, min_point, max_point);
  686.     calculate_extents (Cyl4, min_point, max_point);
  687.     calculate_extents (HV_Floor, min_point, max_point);
  688.  
  689.     obj_size = max_point - min_point;
  690.     obj_center = max_point + min_point;
  691.     obj_center /= 2;
  692.  
  693.     // Make a placeholder object to glue the walls and floor together.
  694.     v3dBlockObject *uni_box = new v3dBlockObject (obj_size);
  695.     uni_box->MoveAbsolute(obj_center);
  696.     uni_box->Hide();
  697.     uni_box->Add(Cyl1);
  698.     uni_box->Add(Cyl2);
  699.     uni_box->Add(Cyl3);
  700.     uni_box->Add(Cyl4);
  701.     uni_box->Add(HV_Floor);
  702.     uni_box->MoveAbsolute(v3dPos(0, 0, 0));
  703.  
  704. #ifdef IR_DEMO
  705.     v3dBlockObject *outside_box = new v3dBlockObject(obj_size);
  706.     outside_box->MoveAbsolute(obj_center);
  707.     outside_box->Hide();
  708.     outside_box->Add(Outside_Cyl1);
  709.     outside_box->Add(Outside_Cyl2);
  710.     outside_box->Add(Outside_Cyl3);
  711.     outside_box->Add(Outside_Cyl4);
  712.     outside_box->Add(Outside_Floor);
  713.     outside_box->MoveAbsolute(v3dPos(0, 0, 0));
  714. #endif    // IR_DEMO
  715.  
  716.     // Scale the room up around the patient.
  717.     Cyl1->Scale(130, v3dPos(0, 0, 0));
  718.     Cyl2->Scale(130, v3dPos(0, 0, 0));
  719.     Cyl3->Scale(130, v3dPos(0, 0, 0));
  720.     Cyl4->Scale(130, v3dPos(0, 0, 0));
  721.     HV_Floor->Scale(130, v3dPos(0, 0, 0));
  722. #ifdef IR_DEMO
  723.     Mash_Floor->Scale(130, v3dPos(0, 0, 0));
  724.     Outside_Cyl1->Scale(1.3, v3dPos(0, 0, 0));
  725.     Outside_Cyl2->Scale(1.3, v3dPos(0, 0, 0));
  726.     Outside_Cyl3->Scale(1.3, v3dPos(0, 0, 0));
  727.     Outside_Cyl4->Scale(1.3, v3dPos(0, 0, 0));
  728.     Outside_Floor->Scale(1.3, v3dPos(0, 0, 0));
  729.  
  730.     // Stretch the room even more for the outside textures.
  731.     Outside_Cyl1->Scale(v3dVector (260, 130, 260), v3dPos(0, 0, 0));
  732.     Outside_Cyl2->Scale(v3dVector (260, 130, 260), v3dPos(0, 0, 0));
  733.     Outside_Cyl3->Scale(v3dVector (260, 130, 260), v3dPos(0, 0, 0));
  734.     Outside_Cyl4->Scale(v3dVector (260, 130, 260), v3dPos(0, 0, 0));
  735.     Outside_Floor->Scale(v3dVector (260, 130, 260), v3dPos(0, 0, 0));
  736. #endif    // IR_DEMO
  737.  
  738. }
  739.  
  740. // Given an object and a previously-initialized min point and max point, this
  741. // function determines if the extents of the object exceed the min or max
  742. // points, and if so then the points are updated accordingly.
  743. void
  744. calculate_extents (v3dObject *object, v3dPos &min_point, v3dPos &max_point)
  745. {
  746.     v3dPos obj_center = object->GetAbsolutePosition();
  747.     v3dDim obj_size = object->GetSize() / 2;
  748.  
  749.     v3dPos obj_min = obj_center - obj_size,
  750.         obj_max = obj_center + obj_size;
  751.  
  752.     if (obj_min.x() < min_point.x())
  753.         min_point.x(obj_min.x());
  754.     if (obj_min.y() < min_point.y())
  755.         min_point.y(obj_min.y());
  756.     if (obj_min.z() < min_point.z())
  757.         min_point.z(obj_min.z());
  758.  
  759.     if (obj_max.x() > max_point.x())
  760.         max_point.x(obj_max.x());
  761.     if (obj_max.y() > max_point.y())
  762.         max_point.y(obj_max.y());
  763.     if (obj_max.z() > max_point.z())
  764.         max_point.z(obj_max.z());
  765. }
  766.  
  767. ////////////////////////////////////////////////////////////////////////////////
  768. // Create patient
  769. ////////////////////////////////////////////////////////////////////////////////
  770. // This function loads the models that make up the patient.
  771. void
  772. create_patient()
  773. {
  774.     cout << "Load patient's body...\n";
  775. //    Patient = new v3dCustomObject("patient.nff");
  776.     Patient = new v3dCustomObject("body.obj");
  777.     Patient->SetColor(v3dColor(73, 74, 216));
  778.     v3dObject *head = new v3dCustomObject("patient_head.obj");
  779.     v3dObject *torso = new v3dCustomObject("torso4.obj");
  780.     v3dObject *inner_torso = new v3dCustomObject("torso4.inside.obj");
  781.     v3dObject *arms = new v3dCustomObject("arms.obj");
  782.     v3dObject *legs = new v3dCustomObject("legs_closed.obj");
  783.     v3dObject *patient_hf = new v3dCustomObject("patient_hf.obj");
  784.     Inner_Abd = new v3dCustomObject("sphere5.obj");
  785.     v3dObject *guts = new v3dCustomObject("Laparoscopy/guts.obj");
  786.     v3dObject *liver = new v3dCustomObject("Laparoscopy/liver.obj");
  787.     v3dObject *rightabd = new v3dCustomObject("Laparoscopy/rightabd.obj");
  788.     v3dObject *leftabd = new v3dCustomObject("Laparoscopy/leftabd.obj");
  789.     Gall_Bladder = new v3dCustomObject("Laparoscopy/gb.obj");
  790.     Gall_Bladder->Hide();
  791.     Under_Gall_Bladder = new v3dCustomObject("Laparoscopy/undergb.obj");
  792.     Under_Gall_Bladder->Hide();
  793.  
  794.     // Set height of the patient to be 1.5 meters and move it 1 meter down.
  795.     v3dDim dim = Patient->GetSize();
  796.     float factor = 1.5/Universe_Unit->ConvToUnits(dim.max());
  797.     Patient->Scale(factor);
  798.     Patient->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  799.     head->Scale(factor, v3dPos(0,0,0));
  800.     head->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  801.     head->SetAbsoluteOrientation(v3dOrient(v3dDir(0, 1, 0)));
  802.     v3dTexture head_texture("peter3.rgb");
  803.     head_texture.SetLinearXYWrapMode(1.);
  804.     head_texture.ApplyToObject(*head);
  805.  
  806.     patient_hf->Scale(factor, v3dPos(0,0,0));
  807.     patient_hf->Translate(Universe_Unit->ConvToPoints(
  808.                         v3dDir(0.0, 1.00, 0)));
  809.     patient_hf->SetColor(v3dColor(222,170,147));
  810.  
  811.     torso->Scale(factor, v3dPos(0, 0, 0));
  812.     torso->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  813.     torso->SetColor(v3dColor(73, 74, 216));
  814.     inner_torso->Scale(factor, v3dPos(0, 0, 0));
  815.     inner_torso->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  816.     inner_torso->SetColor(v3dColor(180, 112, 143));
  817.     arms->Scale(factor, v3dPos(0, 0, 0));
  818.     arms->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  819.     arms->SetColor(v3dColor(73, 74, 216));
  820.     legs->Scale(factor, v3dPos(0, 0, 0));
  821.     legs->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  822.     legs->SetColor(v3dColor(73, 74, 216));
  823.     Inner_Abd->Scale(factor, v3dPos(0, 0, 0));
  824.     Inner_Abd->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  825.     guts->Scale(factor, v3dPos(0, 0, 0));
  826.     guts->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  827.     v3dTexture guts_tex("Laparoscopy/guts.2048.jpg");
  828.     guts_tex.SetTransparency(true);
  829.     guts_tex.ApplyToObject(*guts);
  830.     liver->Scale(factor, v3dPos(0, 0, 0));
  831.     liver->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  832.     v3dTexture liver_tex("Laparoscopy/liver.1024.rgb");
  833.     liver_tex.SetTransparency(true);
  834.     liver_tex.ApplyToObject(*liver);
  835.     rightabd->Scale(factor, v3dPos(0, 0, 0));
  836.     rightabd->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  837.     v3dTexture rightabd_tex("Laparoscopy/rightabd.1024.rgb");
  838.     rightabd_tex.ApplyToObject(*rightabd);
  839.     leftabd->Scale(factor, v3dPos(0, 0, 0));
  840.     leftabd->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  841.     v3dTexture leftabd_tex("Laparoscopy/leftabd.1024.rgb");
  842.     leftabd_tex.ApplyToObject(*leftabd);
  843.     Gall_Bladder->Scale(factor, v3dPos(0, 0, 0));
  844.     Gall_Bladder->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 1.0, 0)));
  845.     Gall_Bladder_Initial_Pos = Gall_Bladder->GetAbsolutePosition();
  846.     Gall_Bladder_Initial_Orient = Gall_Bladder->GetAbsoluteOrientation();
  847.     v3dTexture Gall_Bladder_tex("Laparoscopy/gb.rgb");
  848.     Gall_Bladder_tex.SetTransparency(true);
  849.     Gall_Bladder_tex.ApplyToObject(*Gall_Bladder);
  850.     Under_Gall_Bladder->Scale(factor, v3dPos(0, 0, 0));
  851.     Under_Gall_Bladder->Translate(Universe_Unit->ConvToPoints(
  852.                         v3dDir(0, 1.0, 0)));
  853.     v3dTexture Under_Gall_Bladder_tex("Laparoscopy/undergb.rgb");
  854.     Under_Gall_Bladder_tex.SetTransparency(true);
  855.     Under_Gall_Bladder_tex.ApplyToObject(*Under_Gall_Bladder);
  856.  
  857.     // Load the cautery objects.
  858.     v3dTexture cautery_tex("Laparoscopy/cautery.rgb");
  859.     v3dTexture cautery_flip_tex("Laparoscopy/cautery_flip.rgb");
  860.     for (int i = CAUTERY_TEXTURE_INDEX_START;
  861.             i < CAUTERY_TEXTURE_INDEX_START + CAUTERY_NUM; ++i) {
  862.         char cautery_filename[20];
  863.         sprintf(cautery_filename, "Laparoscopy/cautery%d.obj", i);
  864.         v3dCustomObject *cautery_obj =
  865.             new v3dCustomObject(cautery_filename);
  866.         cautery_obj->Scale(factor, v3dPos(0, 0, 0));
  867.         cautery_obj->Translate(Universe_Unit->ConvToPoints(
  868.             v3dDir(0, 1.0, 0)));
  869.         cautery_obj->Hide();
  870.         if (i < CAUTERY_TEXTURE_FLIP_NUM)
  871.             cautery_tex.ApplyToObject(*cautery_obj);
  872.         else
  873.             cautery_flip_tex.ApplyToObject(*cautery_obj);
  874.         Cautery_Objects.push_back(cautery_obj);
  875.         Cautery_Status.push_back(false);
  876.     }
  877.  
  878.     // Create an object information record for the patient.
  879.     ObjectInfo *patient_info = new ObjectInfo;
  880.     patient_info->object = Patient;
  881.     patient_info->original_position = Patient->GetAbsolutePosition();
  882.     patient_info->original_orientation = Patient->GetAbsoluteOrientation();
  883.     patient_info->is_interactive = false;
  884.     patient_info->object_ID = 1;
  885.     patient_info->object_type_ID = 0;
  886.     Objects[1] = patient_info;
  887.     Patient->Hide();
  888. }
  889.  
  890. ////////////////////////////////////////////////////////////////////////////////
  891. // Decoration object initialization
  892. ////////////////////////////////////////////////////////////////////////////////
  893.  
  894. bool load_configuration_file(char *config_filename)
  895. {
  896.         // Open the file.
  897.         FILE *fp;
  898.         if ((fp = fopen(config_filename, "r")) == NULL) {
  899.                 // File not found, return false.
  900.                 fprintf (stderr, "Error opening configuration file %s\n",
  901.                                 config_filename);
  902.                 return false;
  903.         }
  904.  
  905.     // Read in the number of object records.
  906.     int num_records;
  907.     if (!skip_comment(fp))
  908.         return false;
  909.     fscanf(fp, "%d\n", &num_records);
  910.     fprintf(stderr, "\n%d object records\n\n", num_records);
  911.  
  912.     for (int i = 0; i < num_records; ++i) {
  913.         if (!skip_comment(fp))
  914.             return false;
  915.         ObjectInfo* object_record = new ObjectInfo;
  916.         // Read in the object record header.
  917.         int object_ID, object_type, interactivity_level,
  918.             pos_reference_frame;
  919.         fscanf(fp, "%d %d %d %d",
  920.             &object_ID, &object_type, &interactivity_level,
  921.             &pos_reference_frame);
  922.         fprintf(stderr, "Object %d:\n\tType %d\t%s\n", object_ID,
  923.             object_type, interactivity_level ? "Interactive"
  924.             : "Non-interactive");
  925.         object_record->object_ID = object_ID;
  926.         object_record->object_type_ID = object_type;
  927.         object_record->is_interactive =
  928.             interactivity_level ? true : false;
  929.  
  930.         // Read in the positional data, if specified.
  931.         fprintf(stderr, "\tPosition: ");
  932.         float px, py, pz;
  933.         int rel_pos_obj_ID;
  934.         if (pos_reference_frame > 0) {
  935.             fscanf(fp, "%f %f %f", &px, &py, &pz);
  936.             fprintf(stderr, "%f %f %f", px, py, pz);
  937.             if (pos_reference_frame == 1) {
  938.                 object_record->original_position =
  939.                     Universe_Unit->ConvToPoints(
  940.                         v3dPos(px, py, pz));
  941.             } else if (pos_reference_frame == 2) {
  942.                 fscanf(fp, "%d", &rel_pos_obj_ID);
  943.                 fprintf(stderr, " (relative to object %d)",                        rel_pos_obj_ID);
  944.                 v3dObject *rel_object =
  945.                     get_object_by_ID(rel_pos_obj_ID);
  946.                 if (rel_object)
  947.                     object_record->original_position =
  948.                         rel_object->GetCoordSystem().LocalPosToAbsolute(Universe_Unit->ConvToPoints(v3dPos(px, py, pz)));
  949.                 else
  950.                     fprintf(stderr, "** Object %d not found **\n", rel_pos_obj_ID);
  951.             }
  952.         } else
  953.             fprintf(stderr, "not specified");
  954.         fprintf(stderr, "\n");
  955.  
  956.         // Read in the orientation type and data, if specified.
  957.         int orient_type;
  958.         int rel_orient_obj_ID;
  959.         float ox, oy, oz, twist;
  960.         fscanf(fp, "%d", &orient_type);
  961.         fprintf(stderr, "\tOrientation: ");
  962.         switch (orient_type) {
  963.             case 0:    // Don't orient
  964.                 fprintf(stderr, "not specified\n");
  965.                 break;
  966.  
  967.             case 1:    // Same as other object
  968.             {
  969.                 fscanf(fp, "%d", &rel_orient_obj_ID);
  970.                 fprintf(stderr, "Same as object %d\n",
  971.                     rel_orient_obj_ID);
  972.                 v3dObject *rel_object =
  973.                     get_object_by_ID(rel_orient_obj_ID);
  974.                 if (rel_object)
  975.                     object_record->original_orientation =
  976.                         rel_object->GetAbsoluteOrientation();
  977.                 else
  978.                     fprintf(stderr, "** Object %d not found **\n", rel_orient_obj_ID);
  979.                 break;
  980.             }
  981.  
  982.             case 2:    // Direction
  983.                 fscanf(fp, "%f %f %f", &ox, &oy, &oz);
  984.                 fprintf(stderr, "Dir: %f %f %f\n", ox, oy, oz);
  985.                 object_record->original_orientation =
  986.                     v3dOrient(v3dDir(ox, oy, oz));
  987.                 break;
  988.  
  989.             case 3:    // Direction and twist
  990.                 fscanf(fp, "%f %f %f %f",
  991.                     &ox, &oy, &oz, &twist);
  992.                 fprintf(stderr, "Dir: %f %f %f Twist: %f\n",
  993.                     ox, oy, oz, twist);
  994.                 object_record->original_orientation =
  995.                     v3dOrient(v3dDir(ox, oy, oz), twist);
  996.                 break;
  997.  
  998.             case 4:    // Euler angles
  999.                 fscanf(fp, "%f %f %f", &ox, &oy, &oz);
  1000.                 fprintf(stderr, "Angles: %f %f %f\n",
  1001.                     ox, oy, oz);
  1002.                 object_record->original_orientation =
  1003.                     v3dOrient(v3dEulerAngle(ox, oy, oz));
  1004.                 break;
  1005.  
  1006.             default: // Unknown
  1007.                 fprintf(stderr,
  1008.                     "** Unknown orientation type: %d **\n",
  1009.                     orient_type);
  1010.                 break;
  1011.         }
  1012.  
  1013.         // Read in the size type and data, if specified.
  1014.         int size_type;
  1015.         float sx, sy, sz, size_factor;
  1016.         fscanf(fp, "%d", &size_type);
  1017.         fprintf(stderr, "\tSize: ");
  1018.         switch (size_type) {
  1019.             case 0:    // Don't size
  1020.                 fprintf(stderr, "not specified\n");
  1021.                 break;
  1022.  
  1023.             case 1:    // Stretch to specified size
  1024.                 fscanf(fp, "%f %f %f", &sx, &sy, &sz);
  1025.                 fprintf(stderr, "%f %f %f\n", sx, sy, sz);
  1026.                 break;
  1027.  
  1028.             case 2:    // Scale longest axis to size
  1029.                 fscanf(fp, "%f", &size_factor);
  1030.                 fprintf(stderr, "Long axis: %f\n", size_factor);
  1031.                 break;
  1032.  
  1033.             default: // Unknown
  1034.                 fprintf(stderr, "** Unknown size type: %d **\n",
  1035.                     size_type);
  1036.                 break;
  1037.         }
  1038.  
  1039.         // Finally, read in the object-specific data.
  1040.         fprintf(stderr, "\tType: ");
  1041.         object_record->object = 0;
  1042.         switch (object_type) {
  1043.             case 1: // Custom object
  1044.             {
  1045.                 char object_filename[80];
  1046.                 fscanf(fp, "%s\n", &object_filename);
  1047.                 fprintf(stderr, "Custom object: %s\n",
  1048.                     object_filename);
  1049.                 object_record->object =
  1050.                     new v3dCustomObject(object_filename);
  1051.                 break;
  1052.             }
  1053.  
  1054.             case 2: // Thick object
  1055.             {
  1056.                 int encased_obj_ID;
  1057.                 float thickness;
  1058.                 fscanf(fp, "%d %f",
  1059.                     &encased_obj_ID, &thickness);
  1060.                 fprintf(stderr, "Thick object: %d %d\n",
  1061.                     encased_obj_ID, thickness);
  1062.                 v3dObject *encased_object =
  1063.                     get_object_by_ID(encased_obj_ID);
  1064.                 if (encased_object)
  1065.                     object_record->object =
  1066.                         new ThickObject(encased_object,
  1067.                         Universe_Unit->ConvToPoints(
  1068.                             thickness));
  1069.                 else
  1070.                     fprintf(stderr, "** Object %d not found **\n", encased_obj_ID);
  1071.                 break;
  1072.             }
  1073.  
  1074.             case 3: // ECG plot
  1075.             {
  1076.                 float dx, dy, dz;
  1077.                 int incr_num, ECG_driver_type;
  1078.                 fscanf(fp, "%f %f %f %d %d",
  1079.                     &dx, &dy, &dz, &incr_num,
  1080.                     &ECG_driver_type);
  1081.                 fprintf(stderr, "ECG plot: Size: %f %f %f\n\tIncrements: %d Driver type: %d\n",
  1082.                     dx, dy, dz, incr_num, ECG_driver_type);
  1083.                 ECGDriver *ECG_driver =
  1084.                     get_ECG_driver(fp, ECG_driver_type);
  1085.                 if (ECG_driver) {
  1086.                     object_record->object = new ECGPlot(
  1087.                         ECG_driver,
  1088.                         Universe_Unit->ConvToPoints(
  1089.                             v3dPos(dx, dy, dz)),
  1090.                         incr_num);
  1091.                 }
  1092.  
  1093.                 break;
  1094.             }
  1095.  
  1096.             case 4: // ECG heart model
  1097.             {
  1098.                 int ECG_driver_type;
  1099.                 char AV_node_filename[80],
  1100.                     SA_node_filename[80],
  1101.                     atria_filename[80],
  1102.                     ventricles_filename[80],
  1103.                     atrial_bundles_filename[80],
  1104.                     ventricular_bundles_filename[80];
  1105.                 fscanf(fp, "%s %s %s %s %s %s %d",
  1106.                     &AV_node_filename,
  1107.                     &SA_node_filename,
  1108.                     &atria_filename,
  1109.                     &ventricles_filename,
  1110.                     &atrial_bundles_filename,
  1111.                     &ventricular_bundles_filename,
  1112.                     &ECG_driver_type);
  1113.                 fprintf(stderr, "ECG heart model: Files:\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\tDriver type: %d",
  1114.                     AV_node_filename,
  1115.                     SA_node_filename,
  1116.                     atria_filename,
  1117.                     ventricles_filename,
  1118.                     atrial_bundles_filename,
  1119.                     ventricular_bundles_filename,
  1120.                     ECG_driver_type);
  1121.                 ECGDriver *ECG_driver =
  1122.                     get_ECG_driver(fp, ECG_driver_type);
  1123.  
  1124.                 if (ECG_driver) {
  1125.                     object_record->object =
  1126.                         new ECGHeartModel(
  1127.                             ECG_driver,
  1128.                             AV_node_filename,
  1129.                             SA_node_filename,
  1130.                             atria_filename,
  1131.                             ventricles_filename,
  1132.                             atrial_bundles_filename,
  1133.                             ventricular_bundles_filename);
  1134.                 }
  1135.  
  1136.                 break;
  1137.             }
  1138.  
  1139.             case 5:    // Simple image sequence viewer
  1140.             {
  1141.                 char display_obj_filename[80],
  1142.                     texture_prefix[80], texture_suffix[80];
  1143.                 int transparency,
  1144.                     first_image_num, last_image_num;
  1145.                 fscanf(fp, "%s %d %s %s %d %d",
  1146.                     &display_obj_filename,
  1147.                     &transparency, &texture_prefix,
  1148.                     &texture_suffix, &first_image_num,
  1149.                     &last_image_num);
  1150.                 fprintf(stderr, "Simple image seqeunce viewer\n\tDisplay object: %s\n\tFirst file: %s%d%s\n\tLast file: %s%d%s\n\t%s\n",
  1151.                     display_obj_filename,
  1152.                     texture_prefix, first_image_num,
  1153.                     texture_suffix, texture_prefix,
  1154.                     last_image_num, texture_suffix,
  1155.                     transparency ? "Transparent" : "Opaque");
  1156.                 v3dObject *panel_obj =
  1157.                     new v3dCustomObject(
  1158.                         display_obj_filename);
  1159.                 object_record->object = new ImageSequenceViewer(
  1160.                     panel_obj, 1, false);
  1161.                 delete panel_obj;
  1162.                 ((ImageSequenceViewer*)object_record->object)->
  1163.                     LoadNewSequence(texture_prefix,
  1164.                         texture_suffix, first_image_num,
  1165.                         last_image_num,
  1166.                         transparency ? true : false);
  1167.                 ((ImageSequenceViewer*)object_record->object)->
  1168.                     Play();
  1169.  
  1170.                 break;
  1171.             }
  1172.  
  1173.             case 6:    // MRI viewer
  1174.             {
  1175.                 char display_obj_filename[80];
  1176.                 int num_panels;
  1177.                 fscanf(fp, "%s %d",
  1178.                     &display_obj_filename, &num_panels);
  1179.                 fprintf(stderr, "MRI viewer\n\tDisplay object: %s Panels: %d\n",
  1180.                     display_obj_filename, num_panels);
  1181.                 v3dObject *panel_obj =
  1182.                     new v3dCustomObject(
  1183.                         display_obj_filename);
  1184.                 MRI_Viewer = new ImageSequenceViewer(
  1185.                     panel_obj, num_panels);
  1186.                 object_record->object = MRI_Viewer;
  1187.                 delete panel_obj;
  1188.                 load_MRI_series(1);
  1189.  
  1190.                 break;
  1191.             }
  1192.  
  1193.             case 7: // Blood pressure plot
  1194.             {
  1195.                 float dx, dy, dz;
  1196.                 int incr_num;
  1197.                 char BP_data_filename[80];
  1198.                 fscanf(fp, "%f %f %f %d %s",
  1199.                     &dx, &dy, &dz, &incr_num,
  1200.                     &BP_data_filename);
  1201.                 fprintf(stderr, "BP plot: Size: %f %f %f\n\tIncrements: %d\n\tData file: %s\n",
  1202.                     dx, dy, dz, incr_num, BP_data_filename);
  1203.                 object_record->object = new BPPlot(
  1204.                     new BPDriver(BP_data_filename),
  1205.                     Universe_Unit->ConvToPoints(
  1206.                         v3dDim(dx, dy, dz)),
  1207.                     incr_num);
  1208.  
  1209.                 break;
  1210.             }
  1211.  
  1212.             case 8: // VideoPanel
  1213.                 object_record->object =
  1214.                     VideoObject::GetInstance();
  1215.                 break;
  1216.  
  1217.             default: // Unknown
  1218.                 fprintf(stderr,
  1219.                     "** Unknown object type: %d **\n",
  1220.                     object_type);
  1221.                 break;
  1222.         }
  1223.  
  1224.         // If the object was created, then move, orient, and scale it,
  1225.         // and add it to the list of objects in the simulation.
  1226.         if (object_record->object) {
  1227.             // First, scale the object.
  1228.             if (size_type == 1) {
  1229.                 object_record->object->Scale(
  1230.                     Universe_Unit->ConvToPoints(
  1231.                         v3dDim(sx, sy, sz)));
  1232.             } else if (size_type == 2) {
  1233.                 object_record->object->Scale(
  1234.                     size_factor /
  1235.                     Universe_Unit->ConvToUnits(
  1236.                     object_record->object->GetSize().max()));
  1237.             }
  1238.  
  1239.             // Next, position the object.
  1240.             if (pos_reference_frame)
  1241.                 object_record->object->MoveAbsolute(
  1242.                     object_record->original_position);
  1243.             else
  1244.                 object_record->original_position =
  1245.                     object_record->object->GetAbsolutePosition();
  1246.  
  1247.             // Next, orient the object.
  1248.             if (orient_type)
  1249.                 object_record->object->SetAbsoluteOrientation(
  1250.                     object_record->original_orientation);
  1251.             else
  1252.                 object_record->original_orientation =
  1253.                     object_record->object->GetAbsoluteOrientation();
  1254.  
  1255.             // Finally, add the object record to the map of records.
  1256.             Objects[object_ID] = object_record;
  1257.             if (object_record->is_interactive)
  1258.                 Interactive_Objects[object_ID] = object_record;
  1259.         } else {
  1260.             fprintf(stderr, "\n\n*** Object %d not created ***\n\n",
  1261.                 object_ID);
  1262.             delete object_record;
  1263.         }
  1264.  
  1265.     }
  1266.  
  1267.     fclose(fp);
  1268.  
  1269.     return true;
  1270. }
  1271.  
  1272. // Skips over blank lines and comments in the configuration file, i.e., lines
  1273. // that begin with "#".
  1274. // Returns true if there is something to be read, false if the EOF is reached.
  1275. bool
  1276. skip_comment(FILE *infile)
  1277. {
  1278.     int curr;
  1279.  
  1280.     do {
  1281.         // Skip any whitespace.
  1282.         do {
  1283.             curr = getc(infile);
  1284.         } while (!feof(infile) && isspace(curr));
  1285.  
  1286.         if (curr == '#')
  1287.             // Read to the end of the line.
  1288.             while (!feof(infile) && ((curr = getc(infile)) != '\n'))
  1289.                 ;
  1290.         else
  1291.             break;
  1292.     } while (!feof(infile));
  1293.  
  1294.     if (!feof(infile)) {
  1295.         // Put the non-comment character back in the input stream.
  1296.         ungetc(curr, infile);
  1297.         return true;
  1298.     } else
  1299.         return false;
  1300. }
  1301.  
  1302. // Given an object ID, this function returns a pointer to the object by
  1303. // checking the object record map.
  1304. v3dObject*
  1305. get_object_by_ID(int object_ID)
  1306. {
  1307.     ObjectMap::iterator finder = Objects.find(object_ID);
  1308.  
  1309.     if (finder == Objects.end())
  1310.         return 0;
  1311.     else
  1312.         return (*finder).second->object;
  1313. }
  1314.  
  1315. // Given a file pointer to an open configuration file and an ECG driver
  1316. // type, this function will read in the ECG driver parameters from the file
  1317. // and return a pointer to the driver.
  1318. ECGDriver*
  1319. get_ECG_driver(FILE* fp, int ECG_driver_type)
  1320. {
  1321.     if (ECG_driver_type == 0) {
  1322.         // Create a new ECG_driver.
  1323.         char sequence_filename[80];
  1324.         float min_val, max_val;
  1325.         fscanf(fp, "%s %f %f", &sequence_filename,
  1326.             &min_val, &max_val);
  1327.         fprintf(stderr, "\tSequence file: %s\n\tMin value: %f Max value: %f\n",
  1328.             sequence_filename, min_val, max_val);
  1329.         return new ECGDriver(sequence_filename, min_val, max_val);
  1330.     } else if ((ECG_driver_type == 1) || (ECG_driver_type == 2)) {
  1331.         // Use driver from an ECG plot or an ECG heart model
  1332.         int other_ECG_ID;
  1333.         fscanf(fp, "%d", &other_ECG_ID);
  1334.         fprintf(stderr, "\tOther ECG ID: %d\n", other_ECG_ID);
  1335.         v3dObject* other_ECG = get_object_by_ID(other_ECG_ID);
  1336.         if (!other_ECG) {
  1337.             fprintf(stderr, "** ECG object %d not found **\n",
  1338.                 other_ECG_ID);
  1339.             return 0;
  1340.         }
  1341.         if (ECG_driver_type == 1) {
  1342.             return ((ECGPlot*)other_ECG)->GetECGDriver();
  1343.         }
  1344.     } else {
  1345.         fprintf(stderr, "** Unknown ECG driver type: %d **\n",
  1346.             ECG_driver_type);
  1347.         return 0;
  1348.     }
  1349. }
  1350.  
  1351. ////////////////////////////////////////////////////////////////////////////////
  1352. ////////////////////////////////////////////////////////////////////////////////
  1353. // Moves the hand action point to the position of the camera, then translates
  1354. // it by the hand camera offset.
  1355. void move_hand_to_camera ()
  1356. {
  1357.     Hand_1_AP->MoveAbsolute(
  1358.         v3dSystem::GetCurrentUniverse()->
  1359.             GetCurrentViewPoint()->GetPosition());
  1360.     if (Use_2_Hands)
  1361.         Hand_2_AP->MoveAbsolute(Hand_1_AP->GetAbsolutePosition());
  1362. }
  1363.  
  1364. // This is a formula for calculating sensetivity of both sensors.
  1365. // it gives amount of virtual move in respond to the actual  one
  1366. // v_l - length of the world in meters,
  1367. // g_l - legth of the world in dots,
  1368. // res - resolution of sensor in meters
  1369. float
  1370. sens_coef(float v_l, float g_l, float res)
  1371. {
  1372.     return (g_l * res) / v_l;
  1373. }
  1374.  
  1375. ////////////////////////////////////////////////////////////////////////////////
  1376. // Camera management
  1377. ////////////////////////////////////////////////////////////////////////////////
  1378. // CHANGE: Replace constants with numbers based on scale.
  1379. //v3dVector off(0, 3, 0), noff(0, -3, 0);
  1380. //v3dVector off(0, 0.041, 0), noff(0, -0.041, 0);
  1381. v3dVector off(0, 0.05, 0), noff(0, -0.05, 0);
  1382.  
  1383. // Corect viewpoint move it UP!
  1384. void correct_viewpoint(WTobject *obj)
  1385. {
  1386.     static bool first_correct = false;
  1387.     static v3dVector u_noff = Universe_Unit->ConvToPoints(noff);
  1388.  
  1389.     if(first_correct) 
  1390.         v3dSystem::GetCurrentUniverse()->GetCurrentViewPoint()->Translate(u_noff, Frame_VPoint);
  1391.     else 
  1392.         first_correct = true;
  1393. }
  1394.  
  1395. // Corect viewpoint move it DOWN!
  1396. void correct_viewpoint_CB(const v3dEventMessage *message)
  1397. {
  1398.     static v3dVector u_off = Universe_Unit->ConvToPoints(off);
  1399.  
  1400.     v3dSystem::GetCurrentUniverse()->GetCurrentViewPoint()->Translate(u_off, Frame_VPoint);
  1401. }
  1402.  
  1403. // Move the viewpoint for user 2 to coincide with user 2's object
  1404. // representation.
  1405. void
  1406. user_2_vp_move_CB(const v3dEventMessage *message)
  1407. {
  1408.     User_2_View->MoveAbsolute(User_2_Rep->GetAbsolutePosition());
  1409.     User_2_View->SetAbsoluteOrientation(
  1410.         User_2_Rep->GetAbsoluteOrientation());
  1411.     User_2_View->Translate(v3dDir(0,0,5), Frame_VPoint);
  1412. }
  1413.  
  1414. // Move the viewpoint for the endoscope to coincide with the end of the scope.
  1415. void
  1416. endo_vp_move_CB(const v3dEventMessage *message)
  1417. {
  1418.     v3dPos endo_pos;
  1419.     Endo_View->MoveAbsolute(Endoscope->GetEndPosition());
  1420.     Endo_View->SetDirection(
  1421.         Endoscope->GetAbsoluteOrientation().GetDirection() * -1);
  1422. }
  1423.  
  1424. ////////////////////////////////////////////////////////////////////////////////
  1425. // MRI viewer management
  1426. ////////////////////////////////////////////////////////////////////////////////
  1427.  
  1428. bool
  1429. load_MRI_series(int new_series_number)
  1430. {
  1431.     if (!MRI_Viewer)
  1432.         return;
  1433.  
  1434.     if (new_series_number > NUM_MRI_SERIES)
  1435.         return false;
  1436.  
  1437. fprintf (stderr, "MRI series: %d  # of images: %d\n", new_series_number+1,
  1438. Num_MRI_Textures_Per_Series[new_series_number]);
  1439.  
  1440.     char MRI_file_prefix[80];
  1441.     sprintf (MRI_file_prefix, "E6248/E6248S%dI", new_series_number+1);
  1442.  
  1443.     Current_MRI_Series = new_series_number;
  1444.     MRI_Viewer->LoadNewSequence(MRI_file_prefix, ".rgb", 1,
  1445.         Num_MRI_Textures_Per_Series[Current_MRI_Series], false,
  1446.         new_series_number + 1);
  1447. }
  1448.  
  1449.  
  1450. ////////////////////////////////////////////////////////////////////////////////
  1451. // Sounds for various events
  1452. ////////////////////////////////////////////////////////////////////////////////
  1453.  
  1454. void object_select_handler(const v3dEventMessage* message)
  1455. {
  1456.     Sound_Client->SendSound("click1");
  1457. }
  1458.  
  1459. void object_pick_handler(const v3dEventMessage* message)
  1460. {
  1461.     Sound_Client->SendSound("pick1");
  1462. }
  1463.  
  1464. void object_unpick_handler(const v3dEventMessage* message)
  1465. {
  1466.     Sound_Client->SendSound("drop1");
  1467. }
  1468.  
  1469. void object_set_pivot_handler(const v3dEventMessage* message)
  1470. {
  1471.     Sound_Client->SendSound("poke.wav");
  1472. }
  1473.  
  1474. void object_unset_pivot_handler(const v3dEventMessage* message)
  1475. {
  1476.     Sound_Client->SendSound("boing.wav");
  1477. }
  1478.  
  1479. ////////////////////////////////////////////////////////////////////////////////
  1480. // Swich user and endoscopic viewpoints.
  1481. ////////////////////////////////////////////////////////////////////////////////
  1482.  
  1483. // Toggles windows between normal and endoscopic viewpoints.
  1484. void
  1485. switch_viewpoints()
  1486. {
  1487. static bool Endo_View_on = false;
  1488.  
  1489.     if (!Endo_View_on) {    // Switch to endoscopic view.
  1490.         WTwindow_setviewpoint(WLeft,
  1491.             (WTviewpoint *)Endo_View->GetImplement()->GetHandler());
  1492.         WTwindow_setviewangle(WLeft, ENDO_VIEW_ANGLE);
  1493.         if (Show_Endo_View) {
  1494.             WTwindow_setviewpoint(WLeft_Endo,
  1495.                 (WTviewpoint *)User_1_View->GetImplement()->GetHandler());
  1496.             WTwindow_setviewangle(WLeft_Endo, Normal_View_Angle);
  1497.         }
  1498.         if (Show_User_2) {
  1499.             WTwindow_setviewpoint(WRight,
  1500.                 (WTviewpoint *)Endo_View->GetImplement()->GetHandler());
  1501.             WTwindow_setviewangle(WRight, ENDO_VIEW_ANGLE);
  1502.             if (Show_Endo_View) {
  1503.                 WTwindow_setviewpoint(WRight_Endo,
  1504.                     (WTviewpoint *)User_2_View->GetImplement()->GetHandler());
  1505.                 WTwindow_setviewangle(WRight_Endo, Normal_View_Angle);
  1506.             }
  1507.         }
  1508.         Endo_View_on = true;
  1509.     } else {        // Switch back to normal view.
  1510.         WTwindow_setviewpoint(WLeft,
  1511.             (WTviewpoint *)User_1_View->GetImplement()->GetHandler());
  1512.         WTwindow_setviewangle(WLeft, Normal_View_Angle);
  1513.         if (Show_Endo_View) {
  1514.             WTwindow_setviewpoint(WLeft_Endo,
  1515.                 (WTviewpoint *)Endo_View->GetImplement()->GetHandler());
  1516.             WTwindow_setviewangle(WLeft_Endo, ENDO_VIEW_ANGLE);
  1517.         }
  1518.         if (Show_User_2) {
  1519.             WTwindow_setviewpoint(WRight,
  1520.                 (WTviewpoint *)User_2_View->GetImplement()->GetHandler());
  1521.             WTwindow_setviewangle(WRight, Normal_View_Angle);
  1522.             if (Show_Endo_View) {
  1523.                 WTwindow_setviewpoint(WRight_Endo,
  1524.                     (WTviewpoint *)Endo_View->GetImplement()->GetHandler());
  1525.                 WTwindow_setviewangle(WRight_Endo, ENDO_VIEW_ANGLE);
  1526.             }
  1527.         }
  1528.         Endo_View_on = false;
  1529.     }
  1530.  
  1531. }
  1532.  
  1533. ////////////////////////////////////////////////////////////////////////////////
  1534. // Mouse handler
  1535. ////////////////////////////////////////////////////////////////////////////////
  1536.  
  1537. void mouse_press_handler(const v3dEventMessage *message)
  1538. {
  1539.     v3dMouse::v3dMouseButtons button =
  1540.         *(v3dMouse::v3dMouseButtons *) message->GetCallData();
  1541.  
  1542.     if (button & v3dMouse::Mouse_L_Button) {
  1543.         Selection_Tracker->TogglePick(Hand_1_AP);
  1544.         Pivot_Tracker->TogglePick(Hand_1_AP);
  1545.     } else if (button & v3dMouse::Mouse_R_Button) {
  1546.         Selection_Tracker->TogglePick(Hand_1_AP);
  1547.         Pivot_Tracker->TogglePick(Hand_1_AP);
  1548.     } else if (button & v3dMouse::Mouse_M_Button) {
  1549.         Selection_Tracker->ToggleHeadStabilization(Hand_1_AP);
  1550.         Pivot_Tracker->TogglePivot(Hand_1_AP);
  1551.     }
  1552. }
  1553.  
  1554. ////////////////////////////////////////////////////////////////////////////////
  1555. // Polhemus button box handler
  1556. ////////////////////////////////////////////////////////////////////////////////
  1557.  
  1558. void button_box_press_handler(const v3dEventMessage *message)
  1559. {
  1560.     v3dButtonBoxButtons buttons
  1561.         = *(v3dButtonBoxButtons *)message->GetCallData();
  1562.  
  1563.     // Check buttons for hand 1.
  1564.     if (buttons & v3dButtonBox::ButtonBox_Button_1) {
  1565.         Selection_Tracker->TogglePick(Hand_1_AP);
  1566.         Pivot_Tracker->TogglePick(Hand_1_AP);
  1567.     } else if (buttons & v3dButtonBox::ButtonBox_Button_3) {
  1568.         // Find if the cauterization tool is being held by
  1569.         // this action point.
  1570.         if (Sound_Client &&
  1571.                 (Pivot_Tracker->GetPickingActionPoint(Electrode) == Hand_1_AP))
  1572.             Sound_Client->SendSound("bop");
  1573. fprintf(stderr, "%d out of %d cauterized\n", Cauterized_Count, CAUTERY_NUM);
  1574.         toggle_jaws(Hand_1_AP);
  1575.     } else if (buttons & v3dButtonBox::ButtonBox_Button_2) {
  1576.         Selection_Tracker->ToggleHeadStabilization(Hand_1_AP);
  1577.         Pivot_Tracker->TogglePivot(Hand_1_AP);
  1578.     }
  1579.  
  1580.     if (Use_2_Hands) {
  1581.         // Check buttons for hand 2.
  1582.         if (buttons & v3dButtonBox::ButtonBox_Button_4) {
  1583.             Selection_Tracker->TogglePick(Hand_2_AP);
  1584.             Pivot_Tracker->TogglePick(Hand_1_AP);
  1585.         } else if (buttons & v3dButtonBox::ButtonBox_Button_6) {
  1586.             // Find if the cauterization tool is being held by
  1587.             // this action point.
  1588.             if (Sound_Client &&
  1589.                     (Pivot_Tracker->GetPickingActionPoint(Electrode) == Hand_1_AP))
  1590.                 Sound_Client->SendSound("bop");
  1591.             toggle_jaws(Hand_2_AP);
  1592.         } else if (buttons & v3dButtonBox::ButtonBox_Button_5) {
  1593.             Selection_Tracker->ToggleHeadStabilization(Hand_2_AP);
  1594.             Pivot_Tracker->TogglePivot(Hand_2_AP);
  1595.         }
  1596.     }
  1597. }
  1598.  
  1599. void button_box_down_handler(const v3dEventMessage *message)
  1600. {
  1601.     v3dButtonBoxButtons buttons
  1602.         = *(v3dButtonBoxButtons *)message->GetCallData();
  1603.  
  1604.     // Check buttons for hand 1.
  1605.     if (buttons & v3dButtonBox::ButtonBox_Button_3) {
  1606.         // Find if the cauterization tool is being held by
  1607.         // this action point.
  1608.         if ((Cauterized_Count < CAUTERY_NUM) &&
  1609.             (Pivot_Tracker->GetPickingActionPoint(Electrode) == Hand_1_AP))
  1610.                 cauterize();
  1611.     }
  1612.     if (Use_2_Hands) {
  1613.         // Check buttons for hand 2.
  1614.         if (buttons & v3dButtonBox::ButtonBox_Button_6) {
  1615.             // Find if the cauterization tool is being held by
  1616.             // this action point.
  1617.             if ((Cauterized_Count < CAUTERY_NUM) &&
  1618.                 (Pivot_Tracker->GetPickingActionPoint(Electrode) == Hand_2_AP))
  1619.                     cauterize();
  1620.         }
  1621.     }
  1622. }
  1623.  
  1624. ////////////////////////////////////////////////////////////////////////////////
  1625. // Cauterization functions
  1626. ////////////////////////////////////////////////////////////////////////////////
  1627.  
  1628. // Checks for collision with the ends of the cautization tool and the
  1629. // cautery objects.  If there is a collision, then the cautery object is
  1630. // shown.  If all cautery objects have been collided with, then the
  1631. // gall bladder object is shown and made available for moving.
  1632. void cauterize()
  1633. {
  1634.     v3dObject *jaws = Electrode->GetExtraObject();
  1635.     for (int i = 0; i < CAUTERY_NUM; ++i) {
  1636.         if (!Cautery_Status[i] &&
  1637.                 Cautery_Objects[i]->Intersects(jaws)) {
  1638. fprintf(stderr, "Intersection with cautery %d\n", i);
  1639.             Cautery_Objects[i]->Show();
  1640.             Cautery_Status[i] = true;
  1641.             ++Cauterized_Count;
  1642.             if (Sound_Client)
  1643.                 Sound_Client->SendSound("pop");
  1644.         }
  1645.     }
  1646.     // See if all cauterizations have been made.
  1647.     if (Cauterized_Count >= CAUTERY_NUM) {
  1648.         Gall_Bladder->Show();
  1649.         Under_Gall_Bladder->Show();
  1650.         if (Sound_Client)
  1651.             Sound_Client->SendSound("applause");
  1652.     }
  1653. }
  1654.  
  1655. // This function is called to toggle the open and closed state of the forceps,
  1656. // and to pick up or drop the call bladder if it has been cauterized.
  1657. void toggle_jaws(v3dActionPoint *hand_AP)
  1658. {
  1659. static bool Forceps_Jaws_Open = true;    // The forceps jaws start out open.
  1660.  
  1661.     // Find if the forceps are being held by this action point.
  1662.     if (Pivot_Tracker->GetPickingActionPoint(Forceps) != hand_AP)
  1663.         return;
  1664.  
  1665.     // Drop the gall bladder if it's picked up.
  1666.     if (Gall_Bladder_Picked && !Forceps_Jaws_Open) {
  1667.         Forceps->GetExtraObject()->Remove(Gall_Bladder);
  1668.         Gall_Bladder_Picked = false;
  1669.     }
  1670.  
  1671.     // Toggle the jaws open/closed.
  1672.     Forceps->SwitchExtraObjects();
  1673.     Forceps_Jaws_Open = !Forceps_Jaws_Open;
  1674.  
  1675.     // See if the gall bladder has been picked by the instrument tip.
  1676.     if ((Cauterized_Count >= CAUTERY_NUM) && !Gall_Bladder_Picked
  1677.             && !Forceps_Jaws_Open)
  1678.         if (Forceps->GetExtraObject()->Intersects(Gall_Bladder)) {
  1679.             Forceps->GetExtraObject()->Add(Gall_Bladder);
  1680.             Gall_Bladder_Picked = true;
  1681.         }
  1682.  
  1683. }
  1684.  
  1685. ////////////////////////////////////////////////////////////////////////////////
  1686. // Keyboard handler
  1687. ////////////////////////////////////////////////////////////////////////////////
  1688.  
  1689. void key_press_handler(const v3dEventMessage *message)
  1690. {
  1691.     int  key = *(KEY_TYPE *) message->GetCallData();
  1692.  
  1693.     v3dUniverse *universe = v3dSystem::GetCurrentUniverse();
  1694.     v3dViewPoint *viewpoint = universe->GetCurrentViewPoint();
  1695.     bool is_playing, is_reverse_playing;
  1696.  
  1697.     switch (key) {
  1698.         case WTKEY_LEFTARROW:    case WTKEY_RIGHTARROW:
  1699.         {
  1700.             x3dGoGoIT *gogo_IT;
  1701.  
  1702.             if (Current_IT_1) {
  1703.                 gogo_IT = (x3dGoGoIT *)Current_IT_1;
  1704.  
  1705.                 if (key == WTKEY_LEFTARROW) {
  1706.                     if (gogo_IT->GetWorkingRadius() > 5) 
  1707.                         gogo_IT->SetWorkingRadius(gogo_IT->GetWorkingRadius()-1);
  1708.                 } else
  1709.                     gogo_IT->SetWorkingRadius (gogo_IT->GetWorkingRadius()+1);
  1710.  
  1711.                 cout << "Radius of working area: " << gogo_IT->GetWorkingRadius() << endl;
  1712.             }
  1713.         }
  1714.         break;
  1715.  
  1716.         case WTKEY_UPARROW: case WTKEY_DOWNARROW:
  1717.         {
  1718.             x3dGoGoIT *gogo_IT;
  1719.  
  1720.             if (Current_IT_1) {
  1721.                 gogo_IT = (x3dGoGoIT *)Current_IT_1;
  1722.  
  1723.             if (key == WTKEY_DOWNARROW) {
  1724.                 gogo_IT->SetNonLinearGainCoefficient (gogo_IT->GetNonLinearGainCoefficient()/1.1);
  1725.             } else
  1726.                 gogo_IT->SetNonLinearGainCoefficient (gogo_IT->GetNonLinearGainCoefficient()*1.1);
  1727.  
  1728.             cout << "Non-linear gain: " << gogo_IT->GetNonLinearGainCoefficient() << endl;
  1729.             }
  1730.         }
  1731.         break;
  1732.  
  1733.         case 'r':    // Reset original size and position for all
  1734.                 // objects in the simulation.
  1735.         {
  1736.             RestoreInfo::RestoreAll();
  1737.             Forceps->UpdatePosition(
  1738.                 Universe_Unit->ConvToPoints(FORCEPS_INITIAL_POS),
  1739.                 v3dOrient());
  1740.             Endoscope->UpdatePosition(
  1741.                 Universe_Unit->ConvToPoints(ENDOSCOPE_INITIAL_POS),
  1742.                 v3dOrient());
  1743.             Electrode->UpdatePosition(
  1744.                 Universe_Unit->ConvToPoints(ELECTRODE_INITIAL_POS),
  1745.                 v3dOrient());
  1746.             for (ObjectMap::iterator it =
  1747.                     Interactive_Objects.begin();
  1748.                     it != Interactive_Objects.end(); ++it) {
  1749.                 ObjectInfo* object_info = (*it).second;
  1750.                 object_info->object->MoveAbsolute(
  1751.                     object_info->original_position);
  1752.                 object_info->object->SetAbsoluteOrientation(
  1753.                     object_info->original_orientation);
  1754.             }
  1755.         }
  1756.         break;
  1757.  
  1758.         case 'c':    // Reset the cauterization procedure.
  1759.         {
  1760. fprintf(stderr, "Reseting cauterization\n");
  1761.             Cauterized_Count = 0;
  1762.             if (Gall_Bladder_Picked) {
  1763.                 Forceps->GetExtraObject()->Remove(Gall_Bladder);
  1764.                 Forceps = 0;
  1765.                 Gall_Bladder_Picked = false;
  1766.             }
  1767.             Gall_Bladder->MoveAbsolute(Gall_Bladder_Initial_Pos);
  1768.             Gall_Bladder->SetAbsoluteOrientation(Gall_Bladder_Initial_Orient);
  1769.             Gall_Bladder->Hide();
  1770.             Under_Gall_Bladder->Hide();
  1771.             for (int i = 0; i < CAUTERY_NUM; ++i) {
  1772.                 Cautery_Objects[i]->Hide();
  1773.                 Cautery_Status[i] = false;
  1774.             }
  1775.         }
  1776.         break;
  1777.  
  1778.         case 'C':    // Finish the cauterization procedure.
  1779.         {
  1780. fprintf(stderr, "Finishing cauterization\n");
  1781.             Cauterized_Count = CAUTERY_NUM;
  1782.             for (int i = 0; i < CAUTERY_NUM; ++i) {
  1783.                 Cautery_Objects[i]->Show();
  1784.                 Cautery_Status[i] = true;
  1785.             }
  1786.             Gall_Bladder->Show();
  1787.             Under_Gall_Bladder->Show();
  1788.             if (Sound_Client)
  1789.                 Sound_Client->SendSound("applause");
  1790.         }
  1791.         break;
  1792.  
  1793.         case 'g':    // Toggle Go-Go interaction technique
  1794.             if (Current_IT_1) {
  1795.                 cout << "Removing Go-Go interaction technique" << endl;
  1796.                 delete Current_IT_1;
  1797.                 Current_IT_1 = 0;
  1798.                 if (Use_2_Hands) {
  1799.                     delete Current_IT_2;
  1800.                     Current_IT_2 = 0;
  1801.                 }
  1802.             } else {
  1803.                 cerr << "Using Go-Go interaction technique" << endl;
  1804.                 Current_IT_1 = new x3dGoGoIT(viewpoint, Hand_1_AP,
  1805.                     Universe_Unit->ConvToPoints(0.1793),
  1806.                     Universe_Unit->ConvToPoints(0.0414),
  1807.                     Universe_Unit->ConvToPoints(0.0414));
  1808.                 if (Use_2_Hands) {
  1809.                     Current_IT_2 = new x3dGoGoIT(viewpoint, Hand_2_AP,
  1810.                         Universe_Unit->ConvToPoints(0.1793),
  1811.                         Universe_Unit->ConvToPoints(0.0414),
  1812.                         Universe_Unit->ConvToPoints(0.0414));
  1813.                 }
  1814.             }
  1815.         break;
  1816.  
  1817.         case 'h':
  1818.             if (Use_2_Hands)
  1819.                 Hand_1_AP->MoveAbsolute(Hand_2_AP->GetAbsolutePosition());
  1820.         break;
  1821.  
  1822.         case 'H':
  1823.             if (Use_2_Hands)
  1824.                 Hand_2_AP->MoveAbsolute(Hand_1_AP->GetAbsolutePosition());
  1825.         break;
  1826.  
  1827.         case '-':    // Move hand down.
  1828.             if (Current_IT_1)
  1829.                 ((x3dGoGoIT *)Current_IT_1)->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 0.1, 0)));
  1830.             else
  1831.                 Hand_1_AP->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 0.1, 0)));
  1832.             if (Use_2_Hands)
  1833.                 if (Current_IT_2)
  1834.                     ((x3dGoGoIT *)Current_IT_2)->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 0.1, 0)));
  1835.                 else
  1836.                     Hand_2_AP->Translate(Universe_Unit->ConvToPoints(v3dDir(0, 0.1, 0)));
  1837.         break;
  1838.  
  1839.         case '=':    // Move hand up.
  1840.             if (Current_IT_1)
  1841.                 ((x3dGoGoIT *)Current_IT_1)->Translate(Universe_Unit->ConvToPoints(v3dDir(0, -0.1, 0)));
  1842.             else
  1843.                 Hand_1_AP->Translate(Universe_Unit->ConvToPoints(v3dDir(0, -0.1, 0)));
  1844.  
  1845.             if (Use_2_Hands)
  1846.                 if (Current_IT_2)
  1847.                     ((x3dGoGoIT *)Current_IT_2)->Translate(Universe_Unit->ConvToPoints(v3dDir(0, -0.1, 0)));
  1848.                 else
  1849.                     Hand_2_AP->Translate(Universe_Unit->ConvToPoints(v3dDir(0, -0.1, 0)));
  1850.         break;
  1851.  
  1852.         case 't':    // Toggle pivoting for selected pivot objects.
  1853.             Pivot_Tracker->TogglePivot(Hand_1_AP);
  1854.         break;
  1855.  
  1856.         case 'e':    // Switch between normal and endoscopic views.
  1857.             switch_viewpoints();
  1858.         break;
  1859.  
  1860.         case 'w':    // Move the endoscopic viewpoint window.
  1861.             if (Show_Endo_View) {
  1862.                 Endo_Window_Current_Pos = (Endo_Window_Current_Pos + 1) % ENDO_WINDOW_MAX_POS;
  1863.                 
  1864.                 WTwindow_setposition(WLeft_Endo,
  1865.                     Endo_View_X[Endo_Window_Current_Pos],
  1866.                     Endo_View_Y[Endo_Window_Current_Pos],
  1867.                     ENDO_VIEW_WIDTH, ENDO_VIEW_HEIGHT);
  1868.             }
  1869.         break;
  1870.  
  1871.         case 'n':    // Move to the next MRI series.
  1872.             if (!MRI_Viewer)
  1873.                 return;
  1874.             is_playing = MRI_Viewer->IsPlaying();
  1875.             is_reverse_playing = MRI_Viewer->IsReversePlaying();
  1876.             load_MRI_series((Current_MRI_Series + 1)
  1877.                             % NUM_MRI_SERIES);
  1878.             if (is_playing)
  1879.                 MRI_Viewer->Play();
  1880.             else if (is_reverse_playing)
  1881.                 MRI_Viewer->ReversePlay();
  1882.         break;
  1883.  
  1884.         case 'N':    // Move to the previous MRI series.
  1885.             if (!MRI_Viewer)
  1886.                 return;
  1887.             is_playing = MRI_Viewer->IsPlaying();
  1888.             if (Current_MRI_Series == 0)
  1889.                 load_MRI_series(NUM_MRI_SERIES - 1);
  1890.             else
  1891.                 load_MRI_series((Current_MRI_Series - 1)
  1892.                             % NUM_MRI_SERIES);
  1893.             if (is_playing)
  1894.                 MRI_Viewer->Play();
  1895.         break;
  1896.  
  1897.         case 'x':     // Go to the next image in the MRI series.
  1898.             if (!MRI_Viewer)
  1899.                 return;
  1900.             MRI_Viewer->NextImage();
  1901.         break;
  1902.  
  1903.         case 'z':     // Go to the previous image in the MRI series.
  1904.             if (!MRI_Viewer)
  1905.                 return;
  1906.             MRI_Viewer->PreviousImage();
  1907.         break;
  1908.  
  1909.         case 'a':    // Decrease the number of panels.
  1910.             if (!MRI_Viewer)
  1911.                 return;
  1912.             MRI_Viewer->SetPanelNum(MRI_Viewer->GetPanelNum() - 1);
  1913.         break;
  1914.  
  1915.         case 's':    // Increase the number of panels.
  1916.             if (!MRI_Viewer)
  1917.                 return;
  1918.             MRI_Viewer->SetPanelNum(MRI_Viewer->GetPanelNum() + 1);
  1919.         break;
  1920.  
  1921.         case '.':
  1922.             if (!MRI_Viewer)
  1923.                 return;
  1924.             MRI_Viewer->TogglePlay();
  1925.         break;
  1926.  
  1927.         case ',':
  1928.             if (!MRI_Viewer)
  1929.                 return;
  1930.             MRI_Viewer->ToggleReversePlay();
  1931.         break;
  1932.  
  1933.         case '/':
  1934.             if (!MRI_Viewer)
  1935.                 return;
  1936.             MRI_Viewer->Stop();
  1937.         break;
  1938.  
  1939.         case 'd':    // Increase the delay;
  1940.                 // slow down the sequence animation.
  1941.             if (!MRI_Viewer)
  1942.                 return;
  1943.             MRI_Viewer->SetDelay(MRI_Viewer->GetDelay() + 1);
  1944.         break;
  1945.  
  1946.         case 'f':    // Decrease the delay;
  1947.                 // speed up the sequence animation.
  1948.             if (!MRI_Viewer)
  1949.                 return;
  1950.             MRI_Viewer->SetDelay(MRI_Viewer->GetDelay() - 1);
  1951.         break;
  1952.  
  1953.         case 'v':    // Make a copy of the current radiology image.
  1954. #if 0
  1955.         {
  1956.             if (!MRI_Viewer)
  1957.                 return;
  1958.             v3dObject *new_image;
  1959.             new_image = MRI_Viewer->CopyCurrentImage();
  1960.             new_image->Translate(
  1961.                 v3dDir(Universe_Unit->ConvToUnits(0.1), 0, 0));
  1962.             TempObject::Add(new_image, Hand_1_AP, &object_collide);
  1963.         }
  1964. #endif
  1965.         break;
  1966.  
  1967.         case 'b':    // Get rid of all the temporary objects.
  1968.             TempObject::RemoveAll();
  1969.         break;
  1970.  
  1971.         case '[':
  1972.             viewpoint->SetParallax(viewpoint->GetParallax() * 1.2);
  1973.             cout << "Parallax: " << viewpoint->GetParallax()
  1974.                 << endl;
  1975.         break;
  1976.  
  1977.         case ']':
  1978.             viewpoint->SetParallax(viewpoint->GetParallax() / 1.2);
  1979.             cout << "Parallax: " << viewpoint->GetParallax()
  1980.                 << endl;
  1981.         break;
  1982.  
  1983.         case 'P':    // Print the frame rate.
  1984.             fprintf (stderr, "Frame rate: %f\n",
  1985.                 WTuniverse_framerate());
  1986.         break;
  1987.  
  1988. #ifdef IR_DEMO
  1989.         case '0':
  1990.         case '1': case '2':
  1991.             {
  1992.  
  1993.             char *pan4, *pan1, *pan2, *pan3;
  1994.  
  1995.             HV_Floor->Show();
  1996.             Mash_Floor->Show();
  1997.             Cyl1->Show();
  1998.             Cyl2->Show();
  1999.             Cyl3->Show();
  2000.             Cyl4->Show();
  2001.  
  2002.             Outside_Cyl1->Hide();
  2003.             Outside_Cyl2->Hide();
  2004.             Outside_Cyl3->Hide();
  2005.             Outside_Cyl4->Hide();
  2006.             Outside_Floor->Hide();
  2007.  
  2008.             universe->SetBgColor(v3dColor(0,0,0));
  2009.  
  2010.             if (key == '1') {
  2011.                 // load mash unit texture 1 for walls
  2012.                 pan1 = "mash.pan.0.rgb";
  2013.                 pan3 = "mash.pan.2.rgb";
  2014.                 pan2 = "mash.pan.1.rgb";
  2015.                 pan4 = "mash.pan.3.rgb";
  2016.                 HV_Floor->Hide();
  2017.                 Mash_Floor->Show();
  2018.             } else if (key == '2') {
  2019.                 // load mash unit texture 1 for walls
  2020.                 pan1 = "m1panel0.rgb";
  2021.                 pan3 = "m1panel2.rgb";
  2022.                 pan2 = "m1panel1.rgb";
  2023.                 pan4 = "m1panel3.rgb";
  2024.                 HV_Floor->Hide();
  2025.                 Mash_Floor->Show();
  2026.             } else if (key == '0') {
  2027.                 // load original ER texture for walls
  2028.                 pan1 = "pan_2.rgb";
  2029.                 pan3 = "pan_0.rgb";
  2030.                 pan2 = "pan_1.rgb";
  2031.                 pan4 = "pan_3.rgb";
  2032.                 Mash_Floor->Hide();
  2033.                 HV_Floor->Show();
  2034.             }
  2035.             delete(HV_1); HV_1 = new v3dTexture(pan1);
  2036.             delete(HV_2); HV_2 = new v3dTexture(pan2);
  2037.             delete(HV_3); HV_3 = new v3dTexture(pan3);
  2038.             delete(HV_4); HV_4 = new v3dTexture(pan4);
  2039.  
  2040.             HV_1->SetLinearXYWrapMode(1.);
  2041.             HV_2->SetLinearXYWrapMode(1.);
  2042.             HV_3->SetLinearXYWrapMode(1.);
  2043.             HV_4->SetLinearXYWrapMode(1.);
  2044.  
  2045.             HV_1->ApplyToObject(*Cyl1);
  2046.             HV_2->ApplyToObject(*Cyl2);
  2047.             HV_3->ApplyToObject(*Cyl3);
  2048.             HV_4->ApplyToObject(*Cyl4);
  2049.         }
  2050.  
  2051.         break;
  2052.  
  2053.         case '3':
  2054.             HV_Floor->Hide();
  2055.             Mash_Floor->Hide();
  2056.             Cyl1->Hide();
  2057.             Cyl2->Hide();
  2058.             Cyl3->Hide();
  2059.             Cyl4->Hide();
  2060.  
  2061.             Outside_Cyl1->Show();
  2062.             Outside_Cyl2->Show();
  2063.             Outside_Cyl3->Show();
  2064.             Outside_Cyl4->Show();
  2065.             Outside_Floor->Show();
  2066.  
  2067.             universe->SetBgColor(v3dColor(60,97,158));
  2068.  
  2069.         break;
  2070. #endif    // IR_DEMO
  2071.  
  2072.         case 'q':
  2073.             cout << "Quit \n";
  2074.             exit(1);
  2075.         break;
  2076.  
  2077.         default:
  2078.             cout << "\nWrong key! Press ? for help.\n";
  2079.     }
  2080. }
  2081.  
  2082.