text: // ----------------------------------------------------------------------------
  //
  //
  // OpenSteer -- Steering Behaviors for Autonomous Characters
  //
  // Copyright (c) 2002-2003, Sony Computer Entertainment America
  // Original author: Craig Reynolds <craig_reynolds@playstation.sony.com>
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the "Software"),
  // to deal in the Software without restriction, including without limitation
  // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  // and/or sell copies of the Software, and to permit persons to whom the
  // Software is furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  // DEALINGS IN THE SOFTWARE.
  //
  //
  // ----------------------------------------------------------------------------
  //
  //
  // OpenSteerDemo
  //
  // This class encapsulates the state of the OpenSteerDemo application and the
  // services it provides to its plug-ins.  It is never instantiated, all of
  // its members are static (belong to the class as a whole.)
  //
  // 10-04-04 bk:  put everything into the OpenSteer namespace
  // 11-14-02 cwr: created 
  //
  //
  // ----------------------------------------------------------------------------
  
  
  #include <algorithm>
  #include <sstream>
  #include <OpenSteer/OpenSteerDemo.h>
  #include <OpenSteer/VectorUtils.cuh>
  
  
  
  // ----------------------------------------------------------------------------
  // keeps track of both "real time" and "simulation time"
  
  
  OpenSteer::Clock OpenSteer::OpenSteerDemo::clock;
  
  
  // ----------------------------------------------------------------------------
  // camera automatically tracks selected vehicle
  
  
  OpenSteer::Camera OpenSteer::OpenSteerDemo::camera;
  
  
  // ----------------------------------------------------------------------------
  // currently selected plug-in (user can choose or cycle through them)
  
  
  OpenSteer::PlugIn* OpenSteer::OpenSteerDemo::selectedPlugIn = NULL;
  
  
  // ----------------------------------------------------------------------------
  // currently selected vehicle.  Generally the one the camera follows and
  // for which additional information may be displayed.  Clicking the mouse
  // near a vehicle causes it to become the Selected Vehicle.
  
  
  unsigned int OpenSteer::OpenSteerDemo::selectedVehicleIndex = 0;
  unsigned int OpenSteer::OpenSteerDemo::maxSelectedVehicleIndex = 1;
  
  // ----------------------------------------------------------------------------
  // phase: identifies current phase of the per-frame update cycle
  
  
  int OpenSteer::OpenSteerDemo::phase = OpenSteer::OpenSteerDemo::overheadPhase;
  
  
  // ----------------------------------------------------------------------------
  // graphical annotation: master on/off switch
  
  
  bool OpenSteer::OpenSteerDemo::enableAnnotation = true;
  
  
  // ----------------------------------------------------------------------------
  // XXX apparently MS VC6 cannot handle initialized static const members,
  // XXX so they have to be initialized not-inline.
  
  
  const int OpenSteer::OpenSteerDemo::overheadPhase = 0;
  const int OpenSteer::OpenSteerDemo::updatePhase = 1;
  const int OpenSteer::OpenSteerDemo::drawPhase = 2;
  
  
  // ----------------------------------------------------------------------------
  // initialize OpenSteerDemo application
  
  namespace {
  
      void printPlugIn (OpenSteer::PlugIn& pi) {std::cout << " " << pi << std::endl;} // XXX
  
  } // anonymous namespace
  
  void 
  OpenSteer::OpenSteerDemo::initialize (void)
  {
      // select the default PlugIn
      selectDefaultPlugIn ();
  
      {
          // XXX this block is for debugging purposes,
          // XXX should it be replaced with something permanent?
  
          std::cout << std::endl << "Known plugins:" << std::endl;   // xxx?
          PlugIn::applyToAll (printPlugIn);                          // xxx?
          std::cout << std::endl;                                    // xxx?
  
          // identify default PlugIn
          if (!selectedPlugIn) errorExit ("no default PlugIn");
          std::cout << std::endl << "Default plugin:" << std::endl;  // xxx?
          std::cout << " " << *selectedPlugIn << std::endl;          // xxx?
          std::cout << std::endl;                                    // xxx?
      }
  
      // initialize the default PlugIn
      openSelectedPlugIn ();
  }
  
  
  // ----------------------------------------------------------------------------
  // main update function: step simulation forward and redraw scene
  
  
  void 
  OpenSteer::OpenSteerDemo::updateSimulationAndRedraw (void)
  {
      // update global simulation clock
      clock.update ();
  
      //  start the phase timer (XXX to accurately measure "overhead" time this
      //  should be in displayFunc, or somehow account for time outside this
      //  routine)
      initPhaseTimers ();
  
      // run selected PlugIn (with simulation's current time and step size)
      updateSelectedPlugIn (clock.getTotalSimulationTime (),
                            clock.getElapsedSimulationTime ());
  
      // redraw selected PlugIn (based on real time)
      redrawSelectedPlugIn (clock.getTotalRealTime (),
                            clock.getElapsedRealTime ());
  }
  
  
  // ----------------------------------------------------------------------------
  // exit OpenSteerDemo with a given text message or error code
  
  
  void 
  OpenSteer::OpenSteerDemo::errorExit (const char* message)
  {
      printMessage (message);
  #ifdef _MSC_VER
  	MessageBox(0, message, "OpenSteerDemo Unfortunate Event", MB_ICONERROR);
  #endif
      exit (-1);
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::exit (int exitCode)
  {
      ::exit (exitCode);
  }
  
  
  // ----------------------------------------------------------------------------
  // select the default PlugIn
  
  
  void 
  OpenSteer::OpenSteerDemo::selectDefaultPlugIn (void)
  {
      PlugIn::sortBySelectionOrder ();
      selectedPlugIn = PlugIn::findDefault ();
  }
  
  
  // ----------------------------------------------------------------------------
  // select the "next" plug-in, cycling through "plug-in selection order"
  
  
  void 
  OpenSteer::OpenSteerDemo::selectNextPlugIn (void)
  {
      closeSelectedPlugIn ();
      selectedPlugIn = selectedPlugIn->next ();
      openSelectedPlugIn ();
  }
  
  
  // ----------------------------------------------------------------------------
  // handle function keys an a per-plug-in basis
  
  
  void 
  OpenSteer::OpenSteerDemo::functionKeyForPlugIn (int keyNumber)
  {
      selectedPlugIn->handleFunctionKeys (keyNumber);
  }
  
  
  // ----------------------------------------------------------------------------
  // return name of currently selected plug-in
  
  
  const char* 
  OpenSteer::OpenSteerDemo::nameOfSelectedPlugIn (void)
  {
      return (selectedPlugIn ? selectedPlugIn->name() : "no PlugIn");
  }
  
  
  // ----------------------------------------------------------------------------
  // open the currently selected plug-in
  
  
  void 
  OpenSteer::OpenSteerDemo::openSelectedPlugIn (void)
  {
      camera.reset ();
      selectedVehicleIndex = 0;
      selectedPlugIn->open ();
  }
  
  
  // ----------------------------------------------------------------------------
  // do a simulation update for the currently selected plug-in
  
  
  void 
  OpenSteer::OpenSteerDemo::updateSelectedPlugIn (const float currentTime,
                                                  const float elapsedTime)
  {
      // switch to Update phase
      pushPhase (updatePhase);
  
      // service queued reset request, if any
      doDelayedResetPlugInXXX ();
  
      // if no vehicle is selected, and some exist, select the first one
      //++selectedVehicleIndex %= maxSelectedVehicleIndex;
  
      // invoke selected PlugIn's Update method
      selectedPlugIn->update (currentTime, elapsedTime);
  
      // return to previous phase
      popPhase ();
  }
  
  
  // ----------------------------------------------------------------------------
  // redraw graphics for the currently selected plug-in
  
  
  void 
  OpenSteer::OpenSteerDemo::redrawSelectedPlugIn (const float currentTime,
                                                  const float elapsedTime)
  {
      // switch to Draw phase
      pushPhase (drawPhase);
  
      // invoke selected PlugIn's Draw method
      selectedPlugIn->redraw (currentTime, elapsedTime);
  
      // draw any annotation queued up during selected PlugIn's Update method
      drawAllDeferredLines ();
      drawAllDeferredCirclesOrDisks ();
  
      // return to previous phase
      popPhase ();
  }
  
  
  // ----------------------------------------------------------------------------
  // close the currently selected plug-in
  
  
  void 
  OpenSteer::OpenSteerDemo::closeSelectedPlugIn (void)
  {
      selectedPlugIn->close ();
      selectedVehicleIndex = 0;
  	maxSelectedVehicleIndex = 1;
  }
  
  
  // ----------------------------------------------------------------------------
  // reset the currently selected plug-in
  
  
  void 
  OpenSteer::OpenSteerDemo::resetSelectedPlugIn (void)
  {
      selectedPlugIn->reset ();
  }
  
  
  namespace {
  
      // ----------------------------------------------------------------------------
      // XXX this is used by CaptureTheFlag
      // XXX it was moved here from main.cpp on 12-4-02
      // XXX I'm not sure if this is a useful feature or a bogus hack
      // XXX needs to be reconsidered.
  
  
      bool gDelayedResetPlugInXXX = false;
  
  } // anonymous namespace
      
      
  void 
  OpenSteer::OpenSteerDemo::queueDelayedResetPlugInXXX (void)
  {
      gDelayedResetPlugInXXX = true;
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::doDelayedResetPlugInXXX (void)
  {
      if (gDelayedResetPlugInXXX)
      {
          resetSelectedPlugIn ();
          gDelayedResetPlugInXXX = false;
      }
  }
  
  
  // ----------------------------------------------------------------------------
  // return a group (an STL vector of AbstractVehicle pointers) of all
  // vehicles(/agents/characters) defined by the currently selected PlugIn
  
  
  const OpenSteer::AVGroup& 
  OpenSteer::OpenSteerDemo::allVehiclesOfSelectedPlugIn (void)
  {
      return selectedPlugIn->allVehicles ();
  }
  
  
  // ----------------------------------------------------------------------------
  // select the "next" vehicle: the one listed after the currently selected one
  // in allVehiclesOfSelectedPlugIn
  
  
  void 
  OpenSteer::OpenSteerDemo::selectNextVehicle (void)
  {
      //if (selectedVehicle != NULL)
      //{
      //    // get a container of all vehicles
      //    const AVGroup& all = allVehiclesOfSelectedPlugIn ();
      //    const AVIterator first = all.begin();
      //    const AVIterator last = all.end();
  
      //    // find selected vehicle in container
      //    const AVIterator s = std::find (first, last, selectedVehicle);
  
      //    // normally select the next vehicle in container
      //    selectedVehicle = *(s+1);
  
      //    // if we are at the end of the container, select the first vehicle
      //    if (s == last-1) selectedVehicle = *first;
  
      //    // if the search failed, use NULL
      //    if (s == last) selectedVehicle = NULL;
      //}
  	++selectedVehicleIndex %= maxSelectedVehicleIndex;
  }
  
  
  // ----------------------------------------------------------------------------
  // select vehicle nearest the given screen position (e.g.: of the mouse)
  
  
  void 
  OpenSteer::OpenSteerDemo::selectVehicleNearestScreenPosition (int x, int y)
  {
      //selectedVehicle = findVehicleNearestScreenPosition (x, y);
  }
  
  
  // ----------------------------------------------------------------------------
  // Find the AbstractVehicle whose screen position is nearest the current the
  // mouse position.  Returns NULL if mouse is outside this window or if
  // there are no AbstractVehicle.
  
  
  OpenSteer::AbstractVehicle* 
  OpenSteer::OpenSteerDemo::vehicleNearestToMouse (void)
  {
      return (mouseInWindow ? 
              findVehicleNearestScreenPosition (mouseX, mouseY) :
              NULL);
  }
  
  
  // ----------------------------------------------------------------------------
  // Find the AbstractVehicle whose screen position is nearest the given window
  // coordinates, typically the mouse position.  Returns NULL if there are no
  // AbstractVehicles.
  //
  // This works by constructing a line in 3d space between the camera location
  // and the "mouse point".  Then it measures the distance from that line to the
  // centers of each AbstractVehicle.  It returns the AbstractVehicle whose
  // distance is smallest.
  //
  // xxx Issues: Should the distanceFromLine test happen in "perspective space"
  // xxx or in "screen space"?  Also: I think this would be happy to select a
  // xxx vehicle BEHIND the camera location.
  
  
  OpenSteer::AbstractVehicle* 
  OpenSteer::OpenSteerDemo::findVehicleNearestScreenPosition (int x, int y)
  {
      // find the direction from the camera position to the given pixel
      const float3 direction = directionFromCameraToScreenPosition (x, y);
  
      // iterate over all vehicles to find the one whose center is nearest the
      // "eye-mouse" selection line
      float minDistance = FLT_MAX;       // smallest distance found so far
      AbstractVehicle* nearest = NULL;   // vehicle whose distance is smallest
      const AVGroup& vehicles = allVehiclesOfSelectedPlugIn();
      for (AVIterator i = vehicles.begin(); i != vehicles.end(); i++)
      {
          // distance from this vehicle's center to the selection line:
          const float d = float3_distanceFromLine (make_float3((**i).position()),
                                            make_float3(camera.position()),
                                            direction);
  
          // if this vehicle-to-line distance is the smallest so far,
          // store it and this vehicle in the selection registers.
          if (d < minDistance)
          {
              minDistance = d;
              nearest = *i;
          }
      }
  
      return nearest;
  }
  
  
  // ----------------------------------------------------------------------------
  // for storing most recent mouse state
  
  
  int OpenSteer::OpenSteerDemo::mouseX = 0;
  int OpenSteer::OpenSteerDemo::mouseY = 0;
  bool OpenSteer::OpenSteerDemo::mouseInWindow = false;
  
  
  // ----------------------------------------------------------------------------
  // set a certain initial camera state used by several plug-ins
  
  
  void 
  OpenSteer::OpenSteerDemo::init3dCamera (AbstractVehicle& selected)
  {
      init3dCamera (selected, cameraTargetDistance, camera2dElevation);
  }
  
  void 
  OpenSteer::OpenSteerDemo::init3dCamera (AbstractVehicle& selected,
                                    float distance,
                                    float elevation)
  {
      position3dCamera (selected, distance, elevation);
      camera.fixedDistDistance = distance;
      camera.fixedDistVOffset = elevation;
      camera.mode = Camera::cmFixedDistanceOffset;
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::init2dCamera (AbstractVehicle& selected)
  {
      init2dCamera (selected, cameraTargetDistance, camera2dElevation);
  }
  
  void 
  OpenSteer::OpenSteerDemo::init2dCamera (AbstractVehicle& selected,
                                    float distance,
                                    float elevation)
  {
      position2dCamera (selected, distance, elevation);
      camera.fixedDistDistance = distance;
      camera.fixedDistVOffset = elevation;
      camera.mode = Camera::cmFixedDistanceOffset;
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::position3dCamera (AbstractVehicle& selected)
  {
      position3dCamera (selected, cameraTargetDistance, camera2dElevation);
  }
  
  void 
  OpenSteer::OpenSteerDemo::position3dCamera (AbstractVehicle& selected,
                                              float distance,
                                              float /*elevation*/)
  {
      //selectedVehicle = &selected;
      if (&selected)
      {
          const float3 behind = float3_scalar_multiply(make_float3(selected.forward()), -distance);
          camera.setPosition ( make_float4(float3_add(make_float3(selected.position()), behind), 0.f) );
          camera.target = make_float3(selected.position());
      }
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::position2dCamera (AbstractVehicle& selected)
  {
      position2dCamera (selected, cameraTargetDistance, camera2dElevation);
  }
  
  void 
  OpenSteer::OpenSteerDemo::position2dCamera (AbstractVehicle& selected,
                                              float distance,
                                              float elevation)
  {
      // position the camera as if in 3d:
      position3dCamera (selected, distance, elevation);
  
      // then adjust for 3d:
      float3 position3d = make_float3(camera.position());
      position3d.y += elevation;
      camera.setPosition (make_float4(position3d, 0.f));
  }
  
  
  // ----------------------------------------------------------------------------
  // camera updating utility used by several plug-ins
  
  
  void 
  OpenSteer::OpenSteerDemo::updateCamera (const float currentTime,
                                          const float elapsedTime,
                                          const AbstractVehicle& selected)
  {
      camera.vehicleToTrack = &selected;
      camera.update (currentTime, elapsedTime, clock.getPausedState ());
  }
  
  
  // ----------------------------------------------------------------------------
  // some camera-related default constants
  
  
  const float OpenSteer::OpenSteerDemo::camera2dElevation = 16;
  const float OpenSteer::OpenSteerDemo::cameraTargetDistance = 26;
  const float3 OpenSteer::OpenSteerDemo::cameraTargetOffset = make_float3(0, OpenSteer::OpenSteerDemo::camera2dElevation, 0);
  
  
  // ----------------------------------------------------------------------------
  // ground plane grid-drawing utility used by several plug-ins
  
  
  void 
  OpenSteer::OpenSteerDemo::gridUtility (const float3& gridTarget)
  {
      // round off target to the nearest multiple of 2 (because the
      // checkboard grid with a pitch of 1 tiles with a period of 2)
      // then lower the grid a bit to put it under 2d annotation lines
      const float3 gridCenter = make_float3((round (gridTarget.x * 0.5f) * 2),
  										  (round (gridTarget.y * 0.5f) * 2) - .05f,
  										  (round (gridTarget.z * 0.5f) * 2));
  
      // colors for checkboard
      const float3 gray1 = grayColor (0.27f);
      const float3 gray2 = grayColor (0.30f);
  
      // draw 50x50 checkerboard grid with 50 squares along each side
      drawXZCheckerboardGrid (50, 50, gridCenter, gray1, gray2);
  
      // alternate style
      // drawXZLineGrid (50, 50, gridCenter, black);
  }
  
  
  // ----------------------------------------------------------------------------
  // draws a gray disk on the XZ plane under a given vehicle
  
  
  void 
  OpenSteer::OpenSteerDemo::highlightVehicleUtility (const AbstractVehicle& vehicle)
  {
      if (&vehicle != NULL)
          drawXZDisk (vehicle.radius(), make_float3(vehicle.position()), gGray60, 20);
  }
  
  
  // ----------------------------------------------------------------------------
  // draws a gray circle on the XZ plane under a given vehicle
  
  
  void 
  OpenSteer::OpenSteerDemo::circleHighlightVehicleUtility (const AbstractVehicle& vehicle)
  {
      if (&vehicle != NULL) drawXZCircle (vehicle.radius () * 1.1f,
                                          make_float3(vehicle.position()),
                                          gGray60,
                                          20);
  }
  
  
  // ----------------------------------------------------------------------------
  // draw a box around a vehicle aligned with its local space
  // xxx not used as of 11-20-02
  
  
  void 
  OpenSteer::OpenSteerDemo::drawBoxHighlightOnVehicle (const AbstractVehicle& v,
                                                 const float3 color)
  {
      if (&v)
      {
          const float diameter = v.radius() * 2;
          const float3 size = make_float3(diameter, diameter, diameter);
          drawBoxOutline (v, size, color);
      }
  }
  
  
  // ----------------------------------------------------------------------------
  // draws a colored circle (perpendicular to view axis) around the center
  // of a given vehicle.  The circle's radius is the vehicle's radius times
  // radiusMultiplier.
  
  
  void 
  OpenSteer::OpenSteerDemo::drawCircleHighlightOnVehicle (const AbstractVehicle& v,
                                                    const float radiusMultiplier,
                                                    const float3 color)
  {
      if (&v)
      {
          const float3& cPosition = make_float3(camera.position());
          draw3dCircle  (v.radius() * radiusMultiplier,  // adjusted radius
                         make_float3(v.position()),                   // center
                         float3_subtract(make_float3(v.position()), cPosition),       // view axis
                         color,                          // drawing color
                         20);                            // circle segments
      }
  }
  
  
  // ----------------------------------------------------------------------------
  
  
  void 
  OpenSteer::OpenSteerDemo::printMessage (const char* message)
  {
      std::cout << "OpenSteerDemo: " <<  message << std::endl << std::flush;
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::printMessage (const std::ostringstream& message)
  {
      printMessage (message.str().c_str());
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::printWarning (const char* message)
  {
      std::cout << "OpenSteerDemo: Warning: " <<  message << std::endl << std::flush;
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::printWarning (const std::ostringstream& message)
  {
      printWarning (message.str().c_str());
  }
  
  
  // ------------------------------------------------------------------------
  // print list of known commands
  //
  // XXX this list should be assembled automatically,
  // XXX perhaps from a list of "command" objects created at initialization
  
  
  void 
  OpenSteer::OpenSteerDemo::keyboardMiniHelp (void)
  {
      printMessage ("");
      printMessage ("defined single key commands:");
      printMessage ("  r      restart current PlugIn.");
      printMessage ("  s      select next vehicle.");
      printMessage ("  c      select next camera mode.");
      printMessage ("  f      select next preset frame rate");
      printMessage ("  Tab    select next PlugIn.");
      printMessage ("  a      toggle annotation on/off.");
      printMessage ("  Space  toggle between Run and Pause.");
      printMessage ("  ->     step forward one frame.");
      printMessage ("  Esc    exit.");
      printMessage ("");
  
      // allow PlugIn to print mini help for the function keys it handles
      selectedPlugIn->printMiniHelpForFunctionKeys ();
  }
  
  
  // ----------------------------------------------------------------------------
  // manage OpenSteerDemo phase transitions (xxx and maintain phase timers)
  
  
  int OpenSteer::OpenSteerDemo::phaseStackIndex = 0;
  const int OpenSteer::OpenSteerDemo::phaseStackSize = 5;
  int OpenSteer::OpenSteerDemo::phaseStack [OpenSteer::OpenSteerDemo::phaseStackSize];
  
  
  void 
  OpenSteer::OpenSteerDemo::pushPhase (const int newPhase)
  {
      // update timer for current (old) phase: add in time since last switch
      updatePhaseTimers ();
  
      // save old phase
      phaseStack[phaseStackIndex++] = phase;
  
      // set new phase
      phase = newPhase;
  
      // check for stack overflow
      if (phaseStackIndex >= phaseStackSize) errorExit ("phaseStack overflow");
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::popPhase (void)
  {
      // update timer for current (old) phase: add in time since last switch
      updatePhaseTimers ();
  
      // restore old phase
      phase = phaseStack[--phaseStackIndex];
  }
  
  
  // ----------------------------------------------------------------------------
  
  
  float OpenSteer::OpenSteerDemo::phaseTimerBase = 0;
  float OpenSteer::OpenSteerDemo::phaseTimers [drawPhase+1];
  
  
  void 
  OpenSteer::OpenSteerDemo::initPhaseTimers (void)
  {
      phaseTimers[drawPhase] = 0;
      phaseTimers[updatePhase] = 0;
      phaseTimers[overheadPhase] = 0;
      phaseTimerBase = clock.getTotalRealTime ();
  }
  
  
  void 
  OpenSteer::OpenSteerDemo::updatePhaseTimers (void)
  {
      const float currentRealTime = clock.realTimeSinceFirstClockUpdate();
      phaseTimers[phase] += currentRealTime - phaseTimerBase;
      phaseTimerBase = currentRealTime;
  }
  
  
  // ----------------------------------------------------------------------------
  