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 } }
//------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------ 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); } } } }
//------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------ 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"); }
//------------------------------------------------------------------------ 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(¶ms) || !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]; } } } }
//------------------------------------------------------------------------ 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(¶ms)) { 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(¶ms)) { 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(¶ms)) { 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 }