void CVehicleSeatActionRotateTurret::MaintainPartRotationWorldSpace(EVehicleTurretRotationType eType)
{
	CVehiclePartBase* pPart   = m_rotations[eType].m_pPart;
	IVehiclePart*     pParent = pPart->GetParent();
	IActor*           pActor  = m_pSeat->GetPassengerActor();

	bool remote     = m_pSeat->GetCurrentTransition() == IVehicleSeat::eVT_RemoteUsage;
	bool worldSpace = m_rotations[eType].m_worldSpace && VehicleCVars().v_independentMountedGuns != 0;

	if (worldSpace && pParent && pActor && pActor->IsClient() && !remote)
	{
		// we want to keep the old worldspace rotation
		// therefore we're updating the local transform from it
		// NB: there is no need to clamp here, its done later

		Matrix34 localTM = pParent->GetWorldTM().GetInverted() * Matrix34(m_rotations[eType].m_prevWorldQuat);
		localTM.OrthonormalizeFast(); // precision issue

		const Matrix34 &baseTM = pPart->GetLocalBaseTM();

		if (!Matrix34::IsEquivalent(baseTM,localTM))
		{
			Ang3 anglesCurr(baseTM);
			Ang3 angles(localTM);

			if (eType == eVTRT_Pitch)
			{
				angles.y = anglesCurr.y;
				angles.z = anglesCurr.z;
			}
			else if (eType == eVTRT_Yaw)
			{
				angles.x = anglesCurr.x;
				angles.y = anglesCurr.y;
			}

			localTM.SetRotationXYZ(angles);
			localTM.SetTranslation(baseTM.GetTranslation());
			pPart->SetLocalBaseTM(localTM);

			m_pSeat->ChangedNetworkState(CVehicle::ASPECT_PART_MATRIX);
		}

#if ENABLE_VEHICLE_DEBUG
		if (VehicleCVars().v_debugdraw == eVDB_Parts)
		{
			float color[] = {1,1,1,1};
			Ang3  a(localTM), aBase(baseTM);
			gEnv->pRenderer->Draw2dLabel(200,200,1.4f,color,false,"localAng: %.1f (real: %.1f)", RAD2DEG(a.z), RAD2DEG(aBase.z));
		}
#endif
	}
}
示例#2
0
//------------------------------------------------------------------------
void CVehiclePartLight::ToggleLight(bool enable)
{
	if (enable && !IsEnabled())
  {
    // 0: no lights at all (only intended for debugging etc.)
    // 1: lights only enabled for the local player
    // 2: all lights enabled
    if (VehicleCVars().v_lights == 0
			|| (VehicleCVars().v_lights == 1 && !m_pVehicle->IsPlayerPassenger()))
      return;

		if (m_pHelper && m_damageRatio < 1.0f)
		{ 
      m_slot = GetEntity()->LoadLight(m_slot, &m_light);

      if (m_slot != -1)
			{
				if(m_pMaterial)
				{
					GetEntity()->SetSlotMaterial(m_slot, m_pMaterial);
				}

				if(m_lightViewDistanceRatio > 0)
				{
					SEntitySlotInfo slotInfo;
					if(GetEntity()->GetSlotInfo(m_slot, slotInfo) && (slotInfo.pLight != NULL))
					{
						slotInfo.pLight->SetViewDistRatio(m_lightViewDistanceRatio);
					}
				}
			}
			
		  UpdateLight(0.f);			
		}

		m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_Visible);
    m_enabled = true;
	}
	else if (!enable)
	{ 
    if (m_slot != -1)
    {
      GetEntity()->FreeSlot(m_slot);
      m_slot = -1;
    }  

		m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_NoUpdate);
    m_enabled = false;
	}

}
//------------------------------------------------------------------------
void CVehicleDamageBehaviorEffect::OnDamageEvent(EVehicleDamageBehaviorEvent event, const SVehicleDamageBehaviorEventParams &behaviorParams)
{
	if(event == eVDBE_MaxRatioExceeded || (event == eVDBE_VehicleDestroyed && m_pSharedParams->disableAfterExplosion) || (event == eVDBE_Repair && behaviorParams.componentDamageRatio < m_pSharedParams->damageRatioMin))
	{
		Reset();

		return;
	}

	if(event == eVDBE_Hit || event == eVDBE_ComponentDestroyed || event == eVDBE_Repair || (event == eVDBE_VehicleDestroyed && !m_pSharedParams->disableAfterExplosion))
	{
		if(m_slot == -1 && !(m_pSharedParams->disableAfterExplosion && m_pVehicle->GetStatus().health <= 0.0f) && event != eVDBE_Repair)
		{
#if ENABLE_VEHICLE_DEBUG
			if(VehicleCVars().v_debugdraw == eVDB_Damage)
			{
				CryLog("Vehicle damage %.2f", behaviorParams.componentDamageRatio);
			}
#endif

			LoadEffect(behaviorParams.pVehicleComponent);
		}

		if(m_slot > -1)
		{ 
			UpdateEffect(behaviorParams.randomness, behaviorParams.componentDamageRatio);
		}
	}
}
bool CFlowNode_AISequenceAction_ApproachAndEnterVehicle::GetAnimationTransitionEnabled() const
{
	bool transitionEnabled = !m_fast;

	// check for suppressed animation transition via cvar v_transitionAnimations
	if (VehicleCVars().v_transitionAnimations == 0)
		transitionEnabled = false;

	return transitionEnabled;
}
示例#5
0
//------------------------------------------------------------------------
void CVehiclePartLight::PostInit()
{
  m_pHelper = m_pVehicle->GetHelper(m_pSharedParameters->m_helperPosName.c_str());

  // get Components this Part belongs to. 
  // currently that's only needed for Lights. 
  for (int i=0,nComps=m_pVehicle->GetComponentCount(); i<nComps; ++i)
  {    
    IVehicleComponent* pComponent = m_pVehicle->GetComponent(i);      
    
    for (int j=0,nParts=pComponent->GetPartCount(); j<nParts; ++j)
    {
      if (pComponent->GetPart(j) == this)
      {
        m_components.push_back(pComponent);
        break;
      }
    }
  }
  
  if (VehicleCVars().v_lights_enable_always)
    ToggleLight(true);
}
//------------------------------------------------------------------------
void CVehicleDamageBehaviorEffect::LoadEffect(IVehicleComponent *pComponent)
{
  if(!m_pDamageEffect)
  {
		const SDamageEffect *pDamageEffect = m_pVehicle->GetParticleParams()->GetDamageEffect(m_pSharedParams->effectName.c_str());

		if(!pDamageEffect)
		{
#if ENABLE_VEHICLE_DEBUG
			if(VehicleCVars().v_debugdraw == eVDB_Damage)
			{
				CryLog("Failed to find damage effect %s", m_pSharedParams->effectName.c_str());
			}
#endif

			return;
		}

#if ENABLE_VEHICLE_DEBUG
		if(VehicleCVars().v_debugdraw == eVDB_Damage)
		{
			CryLog("Found effect %s", m_pSharedParams->effectName.c_str());
		}
#endif

    m_pDamageEffect = pDamageEffect;
  }

	if(m_pDamageEffect)
	{
#if ENABLE_VEHICLE_DEBUG
		if(VehicleCVars().v_debugdraw == eVDB_Damage)
		{
			CryLog("Starting vehicle damage effect: %s", m_pDamageEffect->effectName.c_str());
		}
#endif
  
		if(IParticleEffect *pEffect = gEnv->pParticleManager->FindEffect(m_pDamageEffect->effectName.c_str(), "VehicleDamageBehaviorEffect"))
		{
			IEntity *pEntity = m_pVehicle->GetEntity();
	    
			CRY_ASSERT(pEntity);

			m_slot = pEntity->LoadParticleEmitter(m_slot, pEffect, NULL, false, true);

			if(m_pDamageEffect->pHelper)
			{
				Matrix34	tm;

				m_pDamageEffect->pHelper->GetVehicleTM(tm);
	      
				pEntity->SetSlotLocalTM(m_slot, tm);

				if(m_pSharedParams->updateFromHelper)
				{
					m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_AlwaysUpdate);
				}
			}
			else if(pComponent)
			{ 
				Matrix34	tm(IDENTITY);

				tm.SetTranslation(pComponent->GetBounds().GetCenter());

				pEntity->SetSlotLocalTM(m_slot, tm);
			}
		}
	}
}
示例#7
0
//------------------------------------------------------------------------
bool CVehicleDamages::ProcessHit(float& damage, const HitInfo& hitInfo, bool splash)
{
#if ENABLE_VEHICLE_DEBUG
	string displayDamageType = NULL;
#endif
	CVehicleDamages::TDamageMultipliers::const_iterator	ite = m_damageMultipliersByProjectile.find(hitInfo.projectileClassId), end = m_damageMultipliersByProjectile.end();

	bool bFound = false;

	if(ite == end)
	{
		ite = m_damageMultipliersByHitType.find(hitInfo.type), end = m_damageMultipliersByHitType.end();

		if( ite == end )
		{
#if ENABLE_VEHICLE_DEBUG
			displayDamageType = "default";
#endif
			// 0 is the 'default' damage multiplier, check for it if we didn't find the specified one
			ite = m_damageMultipliersByHitType.find((int)CVehicleDamages::DEFAULT_HIT_TYPE);
		}
		else
		{
			bFound = true;

#if ENABLE_VEHICLE_DEBUG
			displayDamageType = "HitType: ";
			displayDamageType += CCryAction::GetCryAction()->GetIGameRulesSystem()->GetCurrentGameRules()->GetHitType(hitInfo.type);
#endif
		}
	}
	else
	{
		bFound = true;

#if ENABLE_VEHICLE_DEBUG
		char str[256];
		if( gEnv->pGame->GetIGameFramework()->GetNetworkSafeClassName( str, sizeof(str), hitInfo.projectileClassId ) )
		{
			displayDamageType = "ProjClass: ";
			displayDamageType += str;
		}
		else
		{
			displayDamageType = "Unknown_Projectile_Type";
		}
#endif
	}
	
	if (bFound)
	{
    const SDamageMultiplier& mult = ite->second;
    damage *= mult.mult * (splash ? mult.splash : 1.f);    
    
#if ENABLE_VEHICLE_DEBUG
    if (VehicleCVars().v_debugdraw == eVDB_Damage)
		{
      CryLog("mults for %s: %.2f, splash %.2f", displayDamageType.c_str(), mult.mult, mult.splash);
		}
#endif
    
    return true;
	}  

  return false;
}
void CVehicleSeatActionRotateTurret::DoUpdate(const float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_ACTION);

	if (gEnv->IsClient() && m_pVehicle->IsProbablyDistant() && !m_pVehicle->GetGameObject()->IsProbablyVisible())
		return;

	// AI use the aim goal to control their rotation (remote usage too)
	if (!m_aimGoal.IsZero())
	{
		UpdateAimGoal();
	}

	// now update each rotation type
	for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
	{
		if (m_rotations[i].m_pPart)
		{
			MaintainPartRotationWorldSpace((EVehicleTurretRotationType)i);
		}
	}

	// Cache the helper position before applying any rotation
	IActor* pActor        = m_pSeat->GetPassengerActor();
	bool    checkRotation = (m_rotTestHelpers[0] && m_rotTestHelpers[1] && pActor);
	Vec3    oldHelperPos  = checkRotation ? m_rotTestHelpers[1]->GetWorldSpaceTranslation() : Vec3(ZERO);

	Matrix34 oldMatrices[eVTRT_NumRotationTypes];

	for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
	{
		if (m_rotations[i].m_pPart)
		{
			oldMatrices[i] = m_rotations[i].m_pPart->GetLocalBaseTM();
			UpdatePartRotation((EVehicleTurretRotationType)i, frameTime);
		}
	}

	// Check for turret collisions
	if (checkRotation)
	{
		// need to test the new rotations before applying them. Sweep a sphere between the two helpers and check for collisions...
		static IPhysicalEntity* pSkipEntities[10];
		int                     numSkip = m_pVehicle->GetSkipEntities(pSkipEntities, 10);
		primitives::sphere      sphere;
		sphere.center = m_rotTestHelpers[0]->GetWorldSpaceTranslation();
		sphere.r      = m_rotTestRadius;

		geom_contact* pContact = NULL;
		Vec3          dir      = m_rotTestHelpers[1]->GetWorldSpaceTranslation() - sphere.center;
		float         hit      = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, dir, ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid, &pContact, 0, (geom_colltype_player << rwi_colltype_bit) | rwi_stop_at_pierceable, 0, 0, 0, pSkipEntities, numSkip);
		if (hit > 0.001f && pContact)
		{
			// there was a collision. check whether the barrel is moving towards the collision point or not... if not, ignore the collision.
#if ENABLE_VEHICLE_DEBUG
			if (VehicleCVars().v_debugdraw > 0)
			{
				CPersistantDebug* pPD = CCryAction::GetCryAction()->GetPersistantDebug();
				pPD->Begin("VehicleCannon", false);

				ColorF col(1.0f, 0.0f, 0.0f, 1.0f);
				if (pContact && hit > 0.0f)
				{
					pPD->AddSphere(pContact->pt, 0.1f, col, 30.0f);
				}
			}
#endif
			Vec3 endPos  = m_rotTestHelpers[1]->GetWorldSpaceTranslation();
			Vec3 moveDir = endPos - oldHelperPos;
			Vec3 hitDir  = pContact->pt - oldHelperPos;

			if (moveDir.Dot(hitDir) > 0.0f)
			{
				// reset as though the rotation never happened.
				for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
				{
					if (m_rotations[i].m_pPart)
					{
						CVehiclePartBase* pPart = m_rotations[i].m_pPart;
						pPart->SetLocalBaseTM(oldMatrices[i]);
						const Matrix34 &worldTM = pPart->GetWorldTM();
						m_rotations[i].m_prevWorldQuat = Quat(worldTM);
						m_rotations[i].m_orientation.Set(Quat(Matrix33(oldMatrices[i])));
					}
				}
			}
		}
	}

	m_aimGoalPriority = 0;
}
示例#9
0
//------------------------------------------------------------------------
void CVehiclePartTread::Update(const float frameTime)
{
	FUNCTION_PROFILER( GetISystem(), PROFILE_ACTION );

	if (!m_pCharInstance)
		return;

	if (m_bForceSetU)
	{
		m_bForceSetU = false;
		m_currentU = m_wantedU + 1.0f;
		UpdateU();
	}

	ISkeletonPose* pSkeletonPose = m_pCharInstance->GetISkeletonPose();    

	pSkeletonPose->SetForceSkeletonUpdate(0);

	if (VehicleCVars().v_staticTreadDeform == 0 && m_pVehicle->GetStatus().speed < 0.001f)
	{
		return;
	}

	if (frameTime > 0.f && 
		(m_damageRatio >= 1.f || !m_pVehicle->GetGameObject()->IsProbablyVisible() || m_pVehicle->IsProbablyDistant()))
	{
		return;
	}

	// we need a tread update in next frame
	pSkeletonPose->SetForceSkeletonUpdate(1);
	m_pVehicle->NeedsUpdate();

	// animate the UV texture according to the wheels speed
	if (m_uvSpeedMultiplier != 0.0f && frameTime > 0.0f)
	{
		IPhysicalEntity* pPhysics = GetEntity()->GetPhysics();
		pe_status_wheel wheelStatus;
		wheelStatus.iWheel = m_lastWheelIndex;

		if (pPhysics && pPhysics->GetStatus(&wheelStatus) != 0)
		{
			m_wantedU += m_uvSpeedMultiplier * (wheelStatus.w * wheelStatus.r * frameTime);
			m_wantedU -= std::floor(m_wantedU);
			UpdateU();
		}
	}

	// deform the tread to follow the wheels
	QuatT absRoot = pSkeletonPose->GetAbsJointByID(0);

	for (TWheelInfoVector::const_iterator ite=m_wheels.begin(), end=m_wheels.end(); ite != end; ++ite)
	{
		const SWheelInfo& wheelInfo = *ite;
		const Matrix34& slotTM = GetEntity()->GetSlotLocalTM(wheelInfo.slot, true);
		VALIDATE_MAT(slotTM);	

		if (m_operatorQueue)
		{
			m_operatorQueue->PushPosition(wheelInfo.jointId,
					IAnimationOperatorQueue::eOp_Override, slotTM.GetTranslation());
		}

#if ENABLE_VEHICLE_DEBUG
		if (VehicleCVars().v_debugdraw == 4)
		{ 
			Vec3 local = GetEntity()->GetWorldTM().GetInverted() * slotTM.GetTranslation();        
			gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(GetEntity()->GetWorldTM() * (local+Vec3((float)sgn(local.x)*0.5f,0.f,0.f)),0.1f,ColorB(0,0,255,255));
		}
#endif
	}
	ISkeletonAnim* pSkeletonAnim = m_pCharInstance->GetISkeletonAnim();
	pSkeletonAnim->PushPoseModifier(VEH_ANIM_POSE_MODIFIER_LAYER, m_operatorQueue, "VehiclePartAnimatedJoint");
}
示例#10
0
//------------------------------------------------------------------------
void CVehiclePartAnimated::SetDrivingProxy(bool bDrive)
{
	IVehicleMovement* pMovement = m_pVehicle->GetMovement();
	if (!(pMovement && pMovement->UseDrivingProxy()))
		return;

	if (0 == m_hullMatId[bDrive]) // 0 means, nothin to do
		return;

	if (!m_pCharInstance)
		return;

	ISkeletonPose* pSkeletonPose = m_pCharInstance->GetISkeletonPose();
	if (!pSkeletonPose)
		return;
	IDefaultSkeleton &rIDefaultSkeleton = m_pCharInstance->GetIDefaultSkeleton();
	IPhysicalEntity*  pPhysics          = m_pVehicle->GetEntity()->GetPhysics();
	if (!pPhysics)
		return;

	int id = rIDefaultSkeleton.GetJointIDByName("hull_proxy");

	if (id < 0)
	{
		m_hullMatId[0] = m_hullMatId[1] = 0;
		return;
	}

	int partid = pSkeletonPose->GetPhysIdOnJoint(id);

	if (partid == -1)
		return;

	pe_params_part params;
	params.partid = partid;
	params.ipart  = -1;
	if (!pPhysics->GetParams(&params) || !params.nMats)
		return;

	phys_geometry* pGeom = params.pPhysGeom;
	if (pGeom && pGeom->surface_idx < pGeom->nMats)
	{
		ISurfaceTypeManager* pSurfaceMan = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();

		// initialize once
		if (m_hullMatId[0] < 0)
		{
			int         idDriving = 0;
			int         idOrig    = pGeom->pMatMapping[pGeom->surface_idx];
			const char* matOrig   = pSurfaceMan->GetSurfaceType(idOrig)->GetName();

			if (strstr(matOrig, "mat_metal"))
				idDriving = pSurfaceMan->GetSurfaceTypeByName("mat_metal_nofric")->GetId();
			else
			{
				string mat(matOrig);
				mat.append("_nofric");

				idDriving = pSurfaceMan->GetSurfaceTypeByName(mat.c_str(), NULL, false)->GetId();
			}

			//if (pDebug->GetIVal())
			//CryLog("%s looking up driving surface replacement for %s (id %i) -> got id %i", m_pVehicle->GetEntity()->GetName(), matOrig, idOrig, idDriving);

			if (idDriving > 0)
			{
				// store old and new id
				m_hullMatId[0] = idOrig;
				m_hullMatId[1] = idDriving;

				/*if (pDebug->GetIVal())
				   {
				   const char* matDriving = pSurfaceMan->GetSurfaceType(idDriving)->GetName();
				   CryLog("%s storing hull matId for swapping: %i (%s) -> %i (%s)", m_pVehicle->GetEntity()->GetName(), m_hullMatId[0], matOrig, m_hullMatId[1], matDriving);
				   }*/
			}
			else
				m_hullMatId[0] = m_hullMatId[1] = 0;
		}

		// only swap if materials available
		if (m_hullMatId[bDrive] > 0)
		{
#if ENABLE_VEHICLE_DEBUG
			if (VehicleCVars().v_debugdraw == eVDB_Parts)
				CryLog("%s swapping hull proxy from %i (%s) to matId %i (%s)", m_pVehicle->GetEntity()->GetName(), m_hullMatId[bDrive ^ 1], pSurfaceMan->GetSurfaceType(m_hullMatId[bDrive ^ 1])->GetName(), m_hullMatId[bDrive], pSurfaceMan->GetSurfaceType(m_hullMatId[bDrive])->GetName());
#endif

			for (int n = 0; n < pGeom->nMats; ++n)
			{
				pGeom->pMatMapping[n] = m_hullMatId[bDrive];
			}
		}
	}
}
示例#11
0
//------------------------------------------------------------------------
void CVehiclePartAnimated::FlagSkeleton(ISkeletonPose* pSkeletonPose,IDefaultSkeleton &rIDefaultSkeleton)
{
	if (!pSkeletonPose)
		return;

	IPhysicalEntity* pPhysics = GetEntity()->GetPhysics();
	if (!pPhysics)
		return;

	string name;
	int    idWater       = rIDefaultSkeleton.GetJointIDByName("proxy_water");
	uint32 buoyancyParts = (idWater != -1) ? 1 : 0;

	uint32 jointCount = rIDefaultSkeleton.GetJointCount();
	for (uint32 i = 0; i < jointCount; ++i)
	{
		int physId = pSkeletonPose->GetPhysIdOnJoint(i);

		if (physId >= 0)
		{
			CheckColltypeHeavy(physId);
			name = rIDefaultSkeleton.GetJointNameByID(i);

			// when water proxy available, remove float from all others
			// if no water proxy, we leave only "proxy" parts floating
			if (idWater != -1)
			{
				if (i == idWater)
				{
					SetFlags(physId, geom_collides, false);
					SetFlagsCollider(physId, 0);
				}
				else
					SetFlags(physId, geom_floats, false);
			}
			else
			{
				if (name.find("proxy") != string::npos)
					++buoyancyParts;
				else
					SetFlags(physId, geom_floats, false);
			}

			// all objects which have a corresponding *_proxy on the skeleton
			// are set to ray collision only
			if (name.find("_proxy") == string::npos)
			{
				name.append("_proxy");
				int proxyId = rIDefaultSkeleton.GetJointIDByName(name.c_str());

				if (proxyId != -1)
				{
					// remove ray collision from hull proxy(s)
					SetFlags(pSkeletonPose->GetPhysIdOnJoint(proxyId), geom_colltype_ray | geom_colltype13, false);

					// get StatObj from main part, to connect proxies foreignData with it
					IStatObj* pStatObj = pSkeletonPose->GetStatObjOnJoint(i);

					if (pStatObj)
					{
						pe_params_part params;
						params.partid = proxyId;
						if (pPhysics->GetParams(&params))
						{
							if (params.pPhysGeom && params.pPhysGeom->pGeom)
								params.pPhysGeom->pGeom->SetForeignData(pStatObj, 0);
						}
					}

					for (int p = 2; p < 6; ++p)
					{
						// check additional proxies, by naming convention _02, .. _05
						char buf[64];
						cry_sprintf(buf, "%s_%02i", name.c_str(), p);

						proxyId = rIDefaultSkeleton.GetJointIDByName(buf);
						if (proxyId == -1)
							break;

						int proxyPhysId = pSkeletonPose->GetPhysIdOnJoint(proxyId);
						if (proxyPhysId == -1)
							continue;

						SetFlags(proxyPhysId, geom_colltype_ray | geom_colltype13, false);

						// connect proxies to main StatObj (needed for bullet tests, decals)
						if (pStatObj)
						{
							pe_params_part params;
							params.partid = proxyPhysId;
							if (pPhysics->GetParams(&params))
							{
								if (params.pPhysGeom && params.pPhysGeom->pGeom)
									params.pPhysGeom->pGeom->SetForeignData(pStatObj, 0);
							}
						}
					}

					// set ray-collision only on the part
					SetFlags(physId, geom_collides | geom_floats, false);
					SetFlags(physId, geom_colltype_ray | geom_colltype13, true);
					SetFlagsCollider(physId, 0);
				}
			}
		}
	}

	if (buoyancyParts == 0)
	{
		// as fallback, use part with largest volume for buoyancy
		int   partId = -1;
		float maxV   = 0.f;

		pe_status_nparts nparts;
		int              numParts = pPhysics->GetStatus(&nparts);

		for (int i = 0; i < numParts; ++i)
		{
			pe_params_part params;
			params.ipart = i;
			if (pPhysics->GetParams(&params))
			{
				float v = (params.pPhysGeomProxy) ? params.pPhysGeomProxy->V : params.pPhysGeom->V;
				if (v > maxV)
				{
					partId = params.partid;
					maxV   = v;
				}
			}
		}

		if (partId != -1)
			SetFlags(partId, geom_floats, true);
		else
			GameWarning("[CVehiclePartAnimated]: <%s> has no buoyancy parts! (Check material for correct physicalization setup properties)",GetEntity()->GetName());
	}

	int jointId, physId;
	if ((jointId = rIDefaultSkeleton.GetJointIDByName("proxy_skirt")) != -1)
	{
		if ((physId = pSkeletonPose->GetPhysIdOnJoint(jointId)) != -1)
		{
			SetFlags(physId, geom_collides | geom_floats, false);
			SetFlags(physId, geom_colltype_ray | geom_colltype13 | geom_colltype_player | geom_colltype_foliage, true);
			SetFlagsCollider(physId, 0);
		}
	}

	// remove collision flags from all _proxy geoms by debug cvar
	// useful for seeing through, testing ray proxies etc
	if (VehicleCVars().v_disable_hull > 0)
	{
		for (uint32 i = 0; i < rIDefaultSkeleton.GetJointCount(); ++i)
		{
			if (strstr(rIDefaultSkeleton.GetJointNameByID(i), "_proxy"))
			{
				SetFlags(pSkeletonPose->GetPhysIdOnJoint(i), geom_collides | geom_floats, false);
				SetFlagsCollider(pSkeletonPose->GetPhysIdOnJoint(i), 0);
			}
		}
	}
}
void CVehiclePartSuspensionPart::Update(const float frameTime)
{
	inherited::Update(frameTime);

	const Matrix34& parentTm = m_pParentPart->GetLocalTM(false);
	const Matrix34& targetTm = m_targetPart->GetLocalTM(false);

	Vec3 pos = parentTm * m_pos0;
 	Vec3 targetPos = (m_ikFlags&k_flagIgnoreTargetRotation) ? (targetTm.GetColumn3() + m_targetOffset) : (targetTm * m_targetOffset);
 	Vec3 dir = targetPos - pos;
	float length = dir.GetLength();
	if (length > 1e-2f)
	{
		Matrix33 rot = Matrix33::CreateRotationV0V1(m_direction0, dir*(1.f/length));
		Matrix33 partRot = rot*Matrix33(m_initialRot);

		if (m_mode==k_modeRotate || m_mode==k_modeSnapToEF)
		{
			if (m_mode==k_modeSnapToEF)
			{
				pos = targetPos - rot * m_direction0;
			}
			Matrix34 tm(partRot, pos);
			SetLocalTM(tm);
		}
		else if (m_mode==k_modeStretch)
		{
			const float scale = length * m_invLength0;
			const Vec3 z = m_direction0;
			const Vec3 sz = m_direction0*(scale-1.f);
			Matrix33 scaleM;
			scaleM.m00 = 1.f+sz.x*z.x; scaleM.m01 =  sz.y*z.x    ; scaleM.m02 =  sz.z*z.x;
			scaleM.m10 = sz.x*z.y    ; scaleM.m11 =  1.f+sz.y*z.y; scaleM.m12 =  sz.z*z.y;
			scaleM.m20 = sz.x*z.z    ; scaleM.m21 =  sz.y*z.z    ; scaleM.m22 =  1.f+sz.z*z.z;
			Matrix34 tm(partRot * scaleM, pos);
			SetLocalTM(tm);
		}
	}

#if !defined(_RELEASE)
	if (VehicleCVars().v_debugSuspensionIK)
	{
		IRenderAuxGeom* pAuxGeom = gEnv->pRenderer->GetIRenderAuxGeom();
		SAuxGeomRenderFlags flags = pAuxGeom->GetRenderFlags();
		SAuxGeomRenderFlags oldFlags = pAuxGeom->GetRenderFlags();
		flags.SetDepthWriteFlag(e_DepthWriteOff);
		flags.SetDepthTestFlag(e_DepthTestOff);
		pAuxGeom->SetRenderFlags(flags);
	
		ColorB colRed(255,0,0,255);
		ColorB colBlue(0,0,255,255);
		ColorB colWhite(255,255,255,255);
		ColorB colGreen(0,255,0,255);

		pos = m_pVehicle->GetEntity()->GetWorldTM() * pos;
		targetPos = m_pVehicle->GetEntity()->GetWorldTM() * targetPos;
		pAuxGeom->DrawSphere(pos, 0.02f, colGreen);
		pAuxGeom->DrawSphere(targetPos, 0.02f, colBlue);
		pAuxGeom->DrawLine(pos, colWhite, targetPos, colWhite);
	}
#endif
}