text: //
  // Copyright (c) 2008-2017 the Urho3D project.
  //
  // 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.
  //
  
  #include "../Core/Context.h"
  #include "../Physics/PhysicsUtils.h"
  #include "../Physics/RigidBody.h"
  #include "../Physics/PhysicsWorld.h"
  #include "../Scene/Scene.h"
  #include "../IO/Log.h"
  #include "../Physics/RaycastVehicle.h"
  
  #include <BulletDynamics/Vehicle/btRaycastVehicle.h>
  #include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
  
  namespace Atomic
  {
  
  struct RaycastVehicleData
  {
      RaycastVehicleData()
      {
          vehicleRayCaster_ = 0;
          vehicle_ = 0;
          added_ = false;
      }
  
      ~RaycastVehicleData()
      {
          if (vehicleRayCaster_)
          {
              delete vehicleRayCaster_;
          }
          vehicleRayCaster_ = 0;
          if (vehicle_)
          {
              if (physWorld_ && added_)
              {
                  btDynamicsWorld* pbtDynWorld = physWorld_->GetWorld();
                  if (pbtDynWorld)
                      pbtDynWorld->removeAction(vehicle_);
                  added_ = false;
              }
              delete vehicle_;
          }
          vehicle_ = 0;
      }
  
      btRaycastVehicle* Get()
      {
          return vehicle_;
      }
  
      void Init(Scene* scene, RigidBody* body, bool enabled)
      {
          int rightIndex = 0;
          int upIndex = 1;
          int forwardIndex = 2;
          PhysicsWorld* pPhysWorld = scene->GetComponent<PhysicsWorld>();
          btDynamicsWorld* pbtDynWorld = pPhysWorld->GetWorld();
          if (!pbtDynWorld)
              return;
  
          // Delete old vehicle & action first
          if (vehicleRayCaster_)
              delete vehicleRayCaster_;
          if (vehicle_)
          {
              if (added_)
                  pbtDynWorld->removeAction(vehicle_);
              delete vehicle_;
          }
  
          vehicleRayCaster_ = new btDefaultVehicleRaycaster(pbtDynWorld);
          btRigidBody* bthullBody = body->GetBody();
          vehicle_ = new btRaycastVehicle(tuning_, bthullBody, vehicleRayCaster_);
          if (enabled)
          {
              pbtDynWorld->addAction(vehicle_);
              added_ = true;
          }
  
          vehicle_->setCoordinateSystem(rightIndex, upIndex, forwardIndex);
          physWorld_ = pPhysWorld;
      }
  
      void SetEnabled(bool enabled)
      {
          if (!physWorld_ || !vehicle_)
              return;
          btDynamicsWorld* pbtDynWorld = physWorld_->GetWorld();
          if (!pbtDynWorld)
              return;
  
          if (enabled && !added_)
          {
              pbtDynWorld->addAction(vehicle_);
              added_ = true;
          }
          else if (!enabled && added_)
          {
              pbtDynWorld->removeAction(vehicle_);
              added_ = false;
          }
      }
  
      WeakPtr<PhysicsWorld> physWorld_;
      btVehicleRaycaster* vehicleRayCaster_;
      btRaycastVehicle* vehicle_;
      btRaycastVehicle::btVehicleTuning tuning_;
      bool added_;
  };
  
  RaycastVehicle::RaycastVehicle(Context* context) : 
      LogicComponent(context)
  {
      // fixed update() for inputs and post update() to sync wheels for rendering
      SetUpdateEventMask(USE_FIXEDUPDATE | USE_FIXEDPOSTUPDATE | USE_POSTUPDATE);
      vehicleData_ = new RaycastVehicleData();
      wheelNodes_.Clear();
      activate_ = false;
      inAirRPM_ = 0.0f;
      maxSideSlipSpeed_ = 4.0f;
  }
  
  RaycastVehicle::~RaycastVehicle()
  {
      delete vehicleData_;
      wheelNodes_.Clear();
  }
  
  const char* wheelElementNames[] =
  {
      "Number of wheels",
      "   Wheel node id",
      "   Wheel direction",
      "   Wheel axle",
      "   Wheel rest length",
      "   Wheel radius",
      "   Wheel is front wheel",
      "   Steering",
      "   Connection point vector",
      "   Original rotation",
      "   Cumulative skid info",
      "   Side skip speed",
      "   Grounded",
      "   Contact position",
      "   Contact normal",
      "   Suspension stiffness",
      "   Damping relaxation",
      "   Damping compression",
      "   Friction slip",
      "   Roll influence",
      "   Engine force",
      "   Brake",
      0
  };
  
  void RaycastVehicle::RegisterObject(Context* context)
  {
      context->RegisterFactory<RaycastVehicle>();
      ATOMIC_MIXED_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Wheel data", GetWheelDataAttr, SetWheelDataAttr,
              VariantVector, Variant::emptyVariantVector,
              wheelElementNames, AM_DEFAULT);
      ATOMIC_ATTRIBUTE("Maximum side slip threshold", float, maxSideSlipSpeed_, 4.0f, AM_DEFAULT);
      ATOMIC_ATTRIBUTE("RPM for wheel motors in air (0=calculate)", float, inAirRPM_, 0.0f, AM_DEFAULT);
  }
  
  void RaycastVehicle::OnSetEnabled()
  {
      if (vehicleData_)
          vehicleData_->SetEnabled(IsEnabledEffective());
  }
  
  void RaycastVehicle::ApplyAttributes()
  {
      int index = 0;
      hullBody_ = node_->GetOrCreateComponent<RigidBody>();
      Scene* scene = GetScene();
      vehicleData_->Init(scene, hullBody_, IsEnabledEffective());
      VariantVector& value = loadedWheelData_;
      int numObjects = value[index++].GetInt();
      int wheelIndex = 0;
      origRotation_.Clear();
      skidInfoCumulative_.Clear();
      wheelSideSlipSpeed_.Clear();
  
      for (int i = 0; i < numObjects; i++)
      {
          int node_id = value[index++].GetInt();
          Vector3 direction = value[index++].GetVector3();
          Vector3 axle = value[index++].GetVector3();
          float restLength = value[index++].GetFloat();
          float radius = value[index++].GetFloat();
          bool isFrontWheel = value[index++].GetBool();
          float steering = value[index++].GetFloat();
          Vector3 connectionPoint = value[index++].GetVector3();
          Quaternion origRotation = value[index++].GetQuaternion();
          float skidInfoC = value[index++].GetFloat();
          float sideSlipSpeed = value[index++].GetFloat();
  
          bool isContact = value[index++].GetBool();
          Vector3 contactPosition = value[index++].GetVector3();
          Vector3 contactNormal = value[index++].GetVector3();
          float suspensionStiffness = value[index++].GetFloat();
          float dampingRelaxation = value[index++].GetFloat();
          float dampingCompression = value[index++].GetFloat();
          float frictionSlip = value[index++].GetFloat();
          float rollInfluence = value[index++].GetFloat();
          float engineForce = value[index++].GetFloat();
          float brake = value[index++].GetFloat();
          float skidInfo = value[index++].GetFloat();
          Node* wheelNode = GetScene()->GetNode(node_id);
          if (!wheelNode)
          {
              ATOMIC_LOGERROR("RaycastVehicle: Incorrect node id = " + String(node_id) + " index: " + String(index));
              continue;
          }
          btRaycastVehicle* vehicle = vehicleData_->Get();
          int id = GetNumWheels();
          btVector3 connectionPointCS0(connectionPoint.x_, connectionPoint.y_, connectionPoint.z_);
          btVector3 wheelDirectionCS0(direction.x_, direction.y_, direction.z_);
          btVector3 wheelAxleCS(axle.x_, axle.y_, axle.z_);
          btWheelInfo& wheel = vehicle->addWheel(connectionPointCS0,
                                  wheelDirectionCS0,
                                  wheelAxleCS,
                                  restLength,
                                  radius,
                                  vehicleData_->tuning_,
                                  isFrontWheel);
          wheelNodes_.Push(wheelNode);
          origRotation_.Push(origRotation);
          skidInfoCumulative_.Push(skidInfoC);
          wheelSideSlipSpeed_.Push(sideSlipSpeed);
          SetSteeringValue(wheelIndex, steering);
          wheel.m_raycastInfo.m_isInContact = isContact;
          wheel.m_raycastInfo.m_contactNormalWS = btVector3(contactNormal.x_, contactNormal.y_, contactNormal.z_);
          wheel.m_raycastInfo.m_contactPointWS = btVector3(contactPosition.x_, contactPosition.y_, contactPosition.z_);
          wheel.m_suspensionStiffness = suspensionStiffness;
          wheel.m_wheelsDampingRelaxation = dampingRelaxation;
          wheel.m_wheelsDampingCompression = dampingCompression;
          wheel.m_frictionSlip = frictionSlip;
          wheel.m_rollInfluence = rollInfluence;
          wheel.m_engineForce = engineForce;
          wheel.m_brake = brake;
          wheel.m_skidInfo = skidInfo;
          wheelIndex++;
      }
      ATOMIC_LOGDEBUG("maxSideSlipSpeed_ value: " + String(maxSideSlipSpeed_));
      ATOMIC_LOGDEBUG("loaded items: " + String(index));
      ATOMIC_LOGDEBUG("loaded wheels: " + String(GetNumWheels()));
  }
  
  void RaycastVehicle::Init()
  {
      hullBody_ = node_->GetOrCreateComponent<RigidBody>();
      Scene* scene = GetScene();
      vehicleData_->Init(scene, hullBody_, IsEnabledEffective());
  }
  
  void RaycastVehicle::FixedUpdate(float timeStep)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      for (int i = 0; i < GetNumWheels(); i++)
      {
          btWheelInfo whInfo = vehicle->getWheelInfo(i);
          if (whInfo.m_engineForce != 0.0f || whInfo.m_steering != 0.0f)
          {
              hullBody_->Activate();
              break;
          }
      }
  }
  
  void RaycastVehicle::PostUpdate(float timeStep)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      for (int i = 0; i < GetNumWheels(); i++)
      {
          vehicle->updateWheelTransform(i, true);
          btTransform transform = vehicle->getWheelTransformWS(i);
          Vector3 origin = ToVector3(transform.getOrigin());
          Quaternion qRot = ToQuaternion(transform.getRotation());
          Node* pWheel = wheelNodes_[i];
          pWheel->SetWorldPosition(origin);
          pWheel->SetWorldRotation(qRot * origRotation_[i]);
      }
  }
  
  void RaycastVehicle::FixedPostUpdate(float timeStep)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      Vector3 velocity = hullBody_->GetLinearVelocity();
      for (int i = 0; i < GetNumWheels(); i++)
      {
          btWheelInfo& whInfo = vehicle->getWheelInfo(i);
          if (!WheelIsGrounded(i) && GetEngineForce(i) != 0.0f)
          {
              float delta;
              if (inAirRPM_ != 0.0f)
              {
                  delta = inAirRPM_ * timeStep / 60.0f;
              }
              else
              {
                  delta = 8.0f * GetEngineForce(i) * timeStep / (hullBody_->GetMass() * GetWheelRadius(i));
              }
              if (Abs(whInfo.m_deltaRotation) < Abs(delta))
              {
                  whInfo.m_rotation += delta - whInfo.m_deltaRotation;
                  whInfo.m_deltaRotation = delta;
              }
              if (skidInfoCumulative_[i] > 0.05f)
              {
                  skidInfoCumulative_[i] -= 0.002;
              }
          }
          else
          {
              skidInfoCumulative_[i] = GetWheelSkidInfo(i);
          }
          wheelSideSlipSpeed_[i] = Abs(ToVector3(whInfo.m_raycastInfo.m_wheelAxleWS).DotProduct(velocity));
          if (wheelSideSlipSpeed_[i] > maxSideSlipSpeed_)
          {
              skidInfoCumulative_[i] = Clamp(skidInfoCumulative_[i], 0.0f, 0.89f);
          }
      }
  }
  
  void RaycastVehicle::SetMaxSideSlipSpeed(float speed)
  {
      maxSideSlipSpeed_ = speed;
  }
  
  float RaycastVehicle::GetMaxSideSlipSpeed() const
  {
      return maxSideSlipSpeed_;
  }
  
  void RaycastVehicle::SetWheelSkidInfoCumulative(int wheel, float skid)
  {
      skidInfoCumulative_[wheel] = skid;
  }
  
  float RaycastVehicle::GetWheelSkidInfoCumulative(int wheel) const
  {
      return skidInfoCumulative_[wheel];
  }
  
  void RaycastVehicle::AddWheel(Node* wheelNode,
                                  Vector3 wheelDirection, Vector3 wheelAxle,
                                  float restLength, float wheelRadius,
                                  bool frontWheel)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      int id = GetNumWheels();
      Vector3 connectionPoint = wheelNode->GetWorldPosition() - node_->GetWorldPosition();
      btVector3 connectionPointCS0(connectionPoint.x_, connectionPoint.y_, connectionPoint.z_);
      btVector3 wheelDirectionCS0(wheelDirection.x_, wheelDirection.y_, wheelDirection.z_);
      btVector3 wheelAxleCS(wheelAxle.x_, wheelAxle.y_, wheelAxle.z_);
      btWheelInfo& wheel = vehicle->addWheel(connectionPointCS0,
                              wheelDirectionCS0,
                              wheelAxleCS,
                              restLength,
                              wheelRadius,
                              vehicleData_->tuning_,
                              frontWheel);
  
      wheelNodes_.Push(wheelNode);
      origRotation_.Push(wheelNode->GetWorldRotation());
      skidInfoCumulative_.Push(1.0f);
      wheelSideSlipSpeed_.Push(0.0f);
      wheel.m_raycastInfo.m_isInContact = false;
  }
  
  void RaycastVehicle::ResetSuspension()
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      vehicle->resetSuspension();
  }
  
  void RaycastVehicle::UpdateWheelTransform(int wheel, bool interpolated)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      vehicle->updateWheelTransform(wheel, interpolated);
  }
  
  Vector3 RaycastVehicle::GetWheelPosition(int wheel)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btTransform transform = vehicle->getWheelTransformWS(wheel);
      Vector3 origin = ToVector3(transform.getOrigin());
      return origin;
  }
  
  Quaternion RaycastVehicle::GetWheelRotation(int wheel)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btTransform transform = vehicle->getWheelTransformWS(wheel);
      Quaternion rotation = ToQuaternion(transform.getRotation());
      return rotation;
  }
  
  Vector3 RaycastVehicle::GetWheelConnectionPoint(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return ToVector3(whInfo.m_chassisConnectionPointCS);
  }
  
  void RaycastVehicle::SetSteeringValue(int wheel, float steeringValue)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      vehicle->setSteeringValue(steeringValue, wheel);
  }
  
  float RaycastVehicle::GetSteeringValue(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_steering;
  }
  
  void RaycastVehicle::SetWheelSuspensionStiffness(int wheel, float stiffness)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_suspensionStiffness = stiffness;
  }
  
  float RaycastVehicle::GetWheelSuspensionStiffness(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_suspensionStiffness;
  }
  
  void RaycastVehicle::SetWheelDampingRelaxation(int wheel, float damping)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_wheelsDampingRelaxation = damping;
  }
  
  float RaycastVehicle::GetWheelDampingRelaxation(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_wheelsDampingRelaxation;
  }
  
  void RaycastVehicle::SetWheelDampingCompression(int wheel, float compression)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_wheelsDampingCompression = compression;
  }
  
  float RaycastVehicle::GetWheelDampingCompression(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_wheelsDampingCompression;
  }
  
  void RaycastVehicle::SetWheelFrictionSlip(int wheel, float slip)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_frictionSlip = slip;
  }
  
  float RaycastVehicle::GetWheelFrictionSlip(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_frictionSlip;
  }
  
  void RaycastVehicle::SetWheelRollInfluence(int wheel, float rollInfluence)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_rollInfluence = rollInfluence;
  }
  
  Vector3 RaycastVehicle::GetContactPosition(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return ToVector3(whInfo.m_raycastInfo.m_contactPointWS);
  }
  
  Vector3 RaycastVehicle::GetContactNormal(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return ToVector3(whInfo.m_raycastInfo.m_contactNormalWS);
  }
  
  float RaycastVehicle::GetWheelSideSlipSpeed(int wheel) const
  {
      return wheelSideSlipSpeed_[wheel];
  }
  
  float RaycastVehicle::GetWheelRollInfluence(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_rollInfluence;
  }
  
  void RaycastVehicle::SetWheelRadius(int wheel, float wheelRadius)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_wheelsRadius = wheelRadius;
  }
  
  float RaycastVehicle::GetWheelRadius(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_wheelsRadius;
  }
  
  void RaycastVehicle::SetEngineForce(int wheel, float force)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      vehicle->applyEngineForce(force, wheel);
  }
  
  float RaycastVehicle::GetEngineForce(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_engineForce;
  }
  
  void RaycastVehicle::SetBrake(int wheel, float force)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      vehicle->setBrake(force, wheel);
  }
  
  float RaycastVehicle::GetBrake(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_brake;
  }
  
  int RaycastVehicle::GetNumWheels() const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      return vehicle->getNumWheels();
  }
  
  Node* RaycastVehicle::GetWheelNode(int wheel) const
  {
      return wheelNodes_[wheel];
  }
  
  void RaycastVehicle::SetMaxSuspensionTravel(int wheel, float maxSuspensionTravel)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_maxSuspensionTravelCm = maxSuspensionTravel;
  }
  
  float RaycastVehicle::GetMaxSuspensionTravel(int wheel)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_maxSuspensionTravelCm;
  }
  
  void RaycastVehicle::SetWheelDirection(int wheel, Vector3 direction)
  {
      btVector3 dir(direction.x_, direction.y_, direction.z_);
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_wheelDirectionCS = dir;
  }
  
  Vector3 RaycastVehicle::GetWheelDirection(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return ToVector3(whInfo.m_wheelDirectionCS);
  }
  
  void RaycastVehicle::SetWheelAxle(int wheel, Vector3 axle)
  {
      btVector3 ax(axle.x_, axle.y_, axle.z_);
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_wheelAxleCS = ax;
  }
  
  Vector3 RaycastVehicle::GetWheelAxle(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return ToVector3(whInfo.m_wheelAxleCS);
  }
  
  void RaycastVehicle::SetWheelRestLength(int wheel, float length)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_suspensionRestLength1 = length;
  }
  
  float RaycastVehicle::GetWheelRestLength(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_suspensionRestLength1;
  }
  
  void RaycastVehicle::SetWheelSkidInfo(int wheel, float factor)
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      whInfo.m_skidInfo = factor;
  }
  
  float RaycastVehicle::GetWheelSkidInfo(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_skidInfo;
  }
  
  bool RaycastVehicle::IsFrontWheel(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo& whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_bIsFrontWheel;
  }
  
  bool RaycastVehicle::WheelIsGrounded(int wheel) const
  {
      btRaycastVehicle* vehicle = vehicleData_->Get();
      btWheelInfo whInfo = vehicle->getWheelInfo(wheel);
      return whInfo.m_raycastInfo.m_isInContact;
  }
  
  void RaycastVehicle::SetInAirRPM(float rpm)
  {
      inAirRPM_ = rpm;
  }
  
  float RaycastVehicle::GetInAirRPM() const
  {
      return inAirRPM_;
  }
  
  void RaycastVehicle::ResetWheels()
  {
      ResetSuspension();
      for (int i = 0; i < GetNumWheels(); i++)
      {
          UpdateWheelTransform(i, true);
          Vector3 origin = GetWheelPosition(i);
          Node* wheelNode = GetWheelNode(i);
          wheelNode->SetWorldPosition(origin);
      }
  }
  
  VariantVector RaycastVehicle::GetWheelDataAttr() const
  {
      VariantVector ret;
      ret.Reserve(GetNumWheels() * 22 + 1);
      ret.Push(GetNumWheels());
      for (int i = 0; i < GetNumWheels(); i++)
      {
          Node* wNode = GetWheelNode(i);
          int node_id = wNode->GetID();
          ATOMIC_LOGDEBUG("RaycastVehicle: Saving node id = " + String(node_id));
          ret.Push(node_id);
          ret.Push(GetWheelDirection(i));
          ret.Push(GetWheelAxle(i));
          ret.Push(GetWheelRestLength(i));
          ret.Push(GetWheelRadius(i));
          ret.Push(IsFrontWheel(i));
          ret.Push(GetSteeringValue(i));
          ret.Push(GetWheelConnectionPoint(i));
          ret.Push(origRotation_[i]);
          ret.Push(GetWheelSkidInfoCumulative(i));
          ret.Push(GetWheelSideSlipSpeed(i));
          ret.Push(WheelIsGrounded(i));
          ret.Push(GetContactPosition(i));
          ret.Push(GetContactNormal(i));       // 14
          ret.Push(GetWheelSuspensionStiffness(i));
          ret.Push(GetWheelDampingRelaxation(i));
          ret.Push(GetWheelDampingCompression(i));
          ret.Push(GetWheelFrictionSlip(i));
          ret.Push(GetWheelRollInfluence(i));
          ret.Push(GetEngineForce(i));
          ret.Push(GetBrake(i));
          ret.Push(GetWheelSkidInfo(i));
      }
      ATOMIC_LOGDEBUG("RaycastVehicle: saved items: " + String(ret.Size()));
      ATOMIC_LOGDEBUG("maxSideSlipSpeed_ value save: " + String(maxSideSlipSpeed_));
      return ret;
  }
  
  void RaycastVehicle::SetWheelDataAttr(const VariantVector& value)
  {
      if (!vehicleData_)
      {
          ATOMIC_LOGERROR("RaycastVehicle: vehicleData doesn't exist");
          return;
      }
      if (value.Size() < 2)
      {
          ATOMIC_LOGERROR("RaycastVehicle: Incorrect vehicleData");
          return;
      }
  
      loadedWheelData_ = value;
  }
  
  } // namespace Urho3D
  