text: // =============================================================================
  // PROJECT CHRONO - http://projectchrono.org
  //
  // Copyright (c) 2014 projectchrono.org
  // All rights reserved.
  //
  // Use of this source code is governed by a BSD-style license that can be found
  // in the LICENSE file at the top level of the distribution and at
  // http://projectchrono.org/license-chrono.txt.
  //
  // =============================================================================
  // Authors: Radu Serban, Rainer Gericke
  // =============================================================================
  //
  // Main driver function for the MAN 7t full model.
  //
  // The vehicle reference frame has Z up, X towards the front of the vehicle, and
  // Y pointing to the left.
  //
  // =============================================================================
  
  #include "chrono/core/ChStream.h"
  #include "chrono/core/ChRealtimeStep.h"
  #include "chrono/utils/ChUtilsInputOutput.h"
  
  #include "chrono_vehicle/ChConfigVehicle.h"
  #include "chrono_vehicle/ChVehicleModelData.h"
  #include "chrono_vehicle/driver/ChDataDriver.h"
  #include "chrono_vehicle/driver/ChIrrGuiDriver.h"
  #include "chrono_vehicle/terrain/RigidTerrain.h"
  #include "chrono_vehicle/wheeled_vehicle/utils/ChWheeledVehicleIrrApp.h"
  
  #include "chrono_models/vehicle/man/MAN_7t.h"
  
  #include "chrono_thirdparty/filesystem/path.h"
  
  using namespace chrono;
  using namespace chrono::irrlicht;
  using namespace chrono::vehicle;
  using namespace chrono::vehicle::man;
  
  // =============================================================================
  
  // Initial vehicle location and orientation
  ChVector<> initLoc(0, 0, 0.7);
  ChQuaternion<> initRot(1, 0, 0, 0);
  
  enum DriverMode { DEFAULT, RECORD, PLAYBACK };
  DriverMode driver_mode = DEFAULT;
  
  // Visualization type for vehicle parts (PRIMITIVES, MESH, or NONE)
  VisualizationType chassis_vis_type = VisualizationType::MESH;
  VisualizationType suspension_vis_type = VisualizationType::PRIMITIVES;
  VisualizationType steering_vis_type = VisualizationType::PRIMITIVES;
  VisualizationType wheel_vis_type = VisualizationType::MESH;
  VisualizationType tire_vis_type = VisualizationType::MESH;
  
  // Collision type for chassis (PRIMITIVES, MESH, or NONE)
  CollisionType chassis_collision_type = CollisionType::NONE;
  
  // Type of tire model (TMEASY)
  TireModelType tire_model = TireModelType::TMEASY;
  
  // Rigid terrain
  RigidTerrain::PatchType terrain_model = RigidTerrain::PatchType::BOX;
  double terrainHeight = 0;      // terrain height (FLAT terrain only)
  double terrainLength = 200.0;  // size in X direction
  double terrainWidth = 200.0;   // size in Y direction
  
  // Point on chassis tracked by the camera
  ChVector<> trackPoint(0.0, 0.0, 1.75);
  
  // Contact method
  ChContactMethod contact_method = ChContactMethod::SMC;
  bool contact_vis = false;
  
  // Simulation step sizes
  double step_size = 1e-3;
  double tire_step_size = step_size;
  
  // Simulation end time
  double t_end = 1000;
  
  // Time interval between two render frames
  double render_step_size = 1.0 / 50;  // FPS = 50
  
  // Output directories
  const std::string out_dir = GetChronoOutputPath() + "MAN_7t";
  const std::string pov_dir = out_dir + "/POVRAY";
  
  // Debug logging
  bool debug_output = false;
  double debug_step_size = 1.0 / 1;  // FPS = 1
  
  // POV-Ray output
  bool povray_output = false;
  
  // =============================================================================
  
  int main(int argc, char* argv[]) {
      GetLog() << "Copyright (c) 2019 projectchrono.org\nChrono version: " << CHRONO_VERSION << "\n\n";
  
      // --------------
      // Create systems
      // --------------
      // Create the City Bus vehicle, set parameters, and initialize
      MAN_7t my_truck;
      my_truck.SetContactMethod(contact_method);
      my_truck.SetChassisCollisionType(chassis_collision_type);
      my_truck.SetChassisFixed(false);
      my_truck.SetInitPosition(ChCoordsys<>(initLoc, initRot));
      my_truck.SetTireType(tire_model);
      my_truck.SetTireStepSize(tire_step_size);
      my_truck.SetShaftBasedDrivetrain(false);
      my_truck.Initialize();
  
      my_truck.SetChassisVisualizationType(chassis_vis_type);
      my_truck.SetSuspensionVisualizationType(suspension_vis_type);
      my_truck.SetSteeringVisualizationType(steering_vis_type);
      my_truck.SetWheelVisualizationType(wheel_vis_type);
      my_truck.SetTireVisualizationType(tire_vis_type);
  
      // Create the terrain
      RigidTerrain terrain(my_truck.GetSystem());
  
      MaterialInfo minfo;
      minfo.mu = 0.9f;
      minfo.cr = 0.01f;
      minfo.Y = 2e7f;
      auto patch_mat = minfo.CreateMaterial(contact_method);
  
      std::shared_ptr<RigidTerrain::Patch> patch;
      switch (terrain_model) {
          case RigidTerrain::PatchType::BOX:
              patch = terrain.AddPatch(patch_mat, ChVector<>(0, 0, 0), ChVector<>(0, 0, 1), terrainLength, terrainWidth);
              patch->SetTexture(vehicle::GetDataFile("terrain/textures/tile4.jpg"), 200, 200);
              break;
          case RigidTerrain::PatchType::HEIGHT_MAP:
              patch = terrain.AddPatch(patch_mat, CSYSNORM, vehicle::GetDataFile("terrain/height_maps/test64.bmp"),
                                       "test64", 128, 128, 0, 4);
              patch->SetTexture(vehicle::GetDataFile("terrain/textures/grass.jpg"), 16, 16);
              break;
          case RigidTerrain::PatchType::MESH:
              patch = terrain.AddPatch(patch_mat, CSYSNORM, vehicle::GetDataFile("terrain/meshes/test.obj"), "test_mesh");
              patch->SetTexture(vehicle::GetDataFile("terrain/textures/grass.jpg"), 100, 100);
              break;
      }
      patch->SetColor(ChColor(0.8f, 0.8f, 0.5f));
      terrain.Initialize();
  
      // Create the vehicle Irrlicht interface
      ChWheeledVehicleIrrApp app(&my_truck.GetVehicle(), L"MAN 7t Truck Demo");
      app.SetSkyBox();
      app.AddTypicalLights(irr::core::vector3df(30.f, -30.f, 100.f), irr::core::vector3df(30.f, 50.f, 100.f), 250, 130);
      app.SetChaseCamera(trackPoint, 10.0, 0.5);
      app.SetTimestep(step_size);
      app.AssetBindAll();
      app.AssetUpdateAll();
  
      // -----------------
      // Initialize output
      // -----------------
  
      if (!filesystem::create_directory(filesystem::path(out_dir))) {
          std::cout << "Error creating directory " << out_dir << std::endl;
          return 1;
      }
      if (povray_output) {
          if (!filesystem::create_directory(filesystem::path(pov_dir))) {
              std::cout << "Error creating directory " << pov_dir << std::endl;
              return 1;
          }
          terrain.ExportMeshPovray(out_dir);
      }
  
      std::string driver_file = out_dir + "/driver_inputs.txt";
      utils::CSV_writer driver_csv(" ");
  
      // ------------------------
      // Create the driver system
      // ------------------------
  
      // Create the interactive driver system
      ChIrrGuiDriver driver(app);
  
      // Set the time response for steering and throttle keyboard inputs.
      double steering_time = 1.0;  // time to go from 0 to +1 (or from 0 to -1)
      double throttle_time = 1.0;  // time to go from 0 to +1
      double braking_time = 0.3;   // time to go from 0 to +1
      driver.SetSteeringDelta(render_step_size / steering_time);
      driver.SetThrottleDelta(render_step_size / throttle_time);
      driver.SetBrakingDelta(render_step_size / braking_time);
  
      // If in playback mode, attach the data file to the driver system and
      // force it to playback the driver inputs.
      if (driver_mode == PLAYBACK) {
          driver.SetInputDataFile(driver_file);
          driver.SetInputMode(ChIrrGuiDriver::DATAFILE);
      }
  
      driver.Initialize();
  
      // ---------------
      // Simulation loop
      // ---------------
  
      if (debug_output) {
          GetLog() << "\n\n============ System Configuration ============\n";
          my_truck.LogHardpointLocations();
      }
  
      my_truck.GetVehicle().LogSubsystemTypes();
      std::cout << "\nVehicle mass: " << my_truck.GetVehicle().GetVehicleMass() << std::endl;
  
      // Number of simulation steps between miscellaneous events
      int render_steps = (int)std::ceil(render_step_size / step_size);
      int debug_steps = (int)std::ceil(debug_step_size / step_size);
  
      // Initialize simulation frame counters
      int step_number = 0;
      int render_frame = 0;
  
      if (contact_vis) {
          app.SetSymbolscale(1e-4);
          app.SetContactsDrawMode(ChIrrTools::eCh_ContactsDrawMode::CONTACT_FORCES);
      }
  
      ChRealtimeStepTimer realtime_timer;
      while (app.GetDevice()->run()) {
          double time = my_truck.GetSystem()->GetChTime();
  
          // End simulation
          if (time >= t_end)
              break;
  
          // Render scene and output POV-Ray data
          if (step_number % render_steps == 0) {
              app.BeginScene(true, true, irr::video::SColor(255, 140, 161, 192));
              app.DrawAll();
              app.EndScene();
  
              if (povray_output) {
                  char filename[100];
                  sprintf(filename, "%s/data_%03d.dat", pov_dir.c_str(), render_frame + 1);
                  utils::WriteShapesPovray(my_truck.GetSystem(), filename);
              }
  
              render_frame++;
          }
  
          // Debug logging
          if (debug_output && step_number % debug_steps == 0) {
              GetLog() << "\n\n============ System Information ============\n";
              GetLog() << "Time = " << time << "\n\n";
              my_truck.DebugLog(OUT_SPRINGS | OUT_SHOCKS | OUT_CONSTRAINTS);
          }
  
          // Collect output data from modules (for inter-module communication)
          ChDriver::Inputs driver_inputs = driver.GetInputs();
  
          // Driver output
          if (driver_mode == RECORD) {
              driver_csv << time << driver_inputs.m_steering << driver_inputs.m_throttle << driver_inputs.m_braking
                         << std::endl;
          }
  
          // Update modules (process inputs from other modules)
          driver.Synchronize(time);
          terrain.Synchronize(time);
          my_truck.Synchronize(time, driver_inputs, terrain);
          app.Synchronize(driver.GetInputModeAsString(), driver_inputs);
  
          // Advance simulation for one timestep for all modules
          driver.Advance(step_size);
          terrain.Advance(step_size);
          my_truck.Advance(step_size);
          app.Advance(step_size);
  
          // Increment frame number
          step_number++;
  
          // Spin in place for real time to catch up
          realtime_timer.Spin(step_size);
      }
  
      if (driver_mode == RECORD) {
          driver_csv.write_to_file(driver_file);
      }
  
      return 0;
  }
  