//------------------------------------------------------------------------ void CVehicleMovementWarrior::Update(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); if (!IsCollapsing()) CVehicleMovementHovercraft::Update(deltaTime); else CVehicleMovementBase::Update(deltaTime); if (IsCollapsing()) { m_collapseTimer += deltaTime; // check platform Vec3 platformPos; if (m_pPlatformPos) platformPos = m_pPlatformPos->GetWorldSpaceTranslation(); else platformPos.zero(); float dist = platformPos.z - gEnv->p3DEngine->GetTerrainElevation(platformPos.x, platformPos.y); if (dist < 1.f) { m_platformDown = true; } // center turret RotatePart(m_pTurret, DEG2RAD(0.f), AXIS_Z, DEG2RAD(2.5f), deltaTime); // take down wing and cannon RotatePart(m_pWing, DEG2RAD(-12.5f), AXIS_X, DEG2RAD(3.f), deltaTime); RotatePart(m_pCannon, DEG2RAD(-20.f), AXIS_X, DEG2RAD(2.5f), deltaTime); if (!m_platformDown) { // handle legs to bring down platform TThrusters::iterator iter; for (iter=m_vecThrusters.begin(); iter!=m_vecThrusters.end(); ++iter) { SThruster* pThruster = *iter; if (pThruster->heightAdaption <= 0.f) { pThruster->hoverHeight = max(0.1f, pThruster->hoverHeight - 0.6f*deltaTime); continue; } else { //if (!pThruster->groundContact) //pThruster->hoverHeight = max(0.1f, pThruster->hoverHeight - 0.2f*deltaTime); } /* // special legs control float collapseSpeed = DEG2RAD(5.f); float maxDistMovable = 1.f/0.8f; float dist = (isneg(pThruster->prevDist)) ? 0.f : pThruster->hoverHeight - pThruster->prevDist; if (isneg(dist)) { collapseSpeed *= max(0.f, 1.f + maxDistMovable*dist); } if (collapseSpeed > 0.f) { float angle = RotatePart(pThruster->pParentPart, DEG2RAD(m_collapsedLegAngle), collapseSpeed, deltaTime); RotatePart(pThruster->pPart, DEG2RAD(m_collapsedFeetAngle), collapseSpeed, deltaTime); } */ } } else { if (!m_collapsed) { Collapsed(true); } } } if (IsPowered() && !IsCollapsed()) { // "normal" legs control here bool bStartComplete = (m_startComplete > 1.5f); float adaptionSpeed = IsCollapsing() ? 0.8f : 1.5f; int t = 0; for (TThrusters::iterator iter=m_vecThrusters.begin(); iter!=m_vecThrusters.end(); ++iter) { SThruster* pThruster = *iter; ++t; if (pThruster->heightAdaption > 0.f && bStartComplete && pThruster->pPart && pThruster->pParentPart) { const char* footName = pThruster->pPart->GetName(); EWarriorMovement mode = eWM_Hovering; float correction = 0.f, maxCorrection = 0.f; // adjust legs float error = 0.f; if (!pThruster->hit) error = pThruster->hoverHeight; // when not hit, correct downwards else if (pThruster->prevDist > 0.f) error = pThruster->prevDist - pThruster->hoverHeight; if (mode != eWM_None && abs(error) > 0.05f) { float speed = max(0.1f, min(1.f, abs(error))) * adaptionSpeed; correction = -sgn(error) * min(speed*deltaTime, abs(error)); // correct up to error // don't correct more than heightAdaption allows maxCorrection = abs((pThruster->heightInitial + sgn(correction)*pThruster->heightAdaption) - pThruster->pos.z); float minCorrection = (pThruster->groundContact) ? 0.f : -maxCorrection; correction = CLAMP(correction, minCorrection, maxCorrection); if (abs(correction) > 0.0001f) { // positive correction for leg, negative for foot Matrix34 legLocal = pThruster->pParentPart->GetLocalBaseTM(); Matrix34 footLocal = pThruster->pPart->GetLocalBaseTM(); float radius = footLocal.GetTranslation().len(); float deltaAngle = correction / radius; // this assumes correction on circle (accurate enough for large radius) Matrix34 legTM = Matrix33(legLocal) * Matrix33::CreateRotationX(deltaAngle); Matrix34 footTM = Matrix33(footLocal) * Matrix33::CreateRotationX(-deltaAngle); legTM.SetTranslation(legLocal.GetTranslation()); footTM.SetTranslation(footLocal.GetTranslation()); pThruster->pParentPart->SetLocalBaseTM(legTM); pThruster->pPart->SetLocalBaseTM(footTM); } } if (IsProfilingMovement()) { static ICVar* pDebugLeg = gEnv->pConsole->GetCVar("warrior_debug_leg"); if (pDebugLeg && pDebugLeg->GetIVal() == t) { //CryLog("hoverErr %.2f, levelErr %.2f, neutralErr %.2f -> %s corr %.3f (max %.2f)", hoverError, levelError, neutralError, sMode, correction, maxCorrection); } } } } } // regain control if (m_collapseTimer > m_recoverTime) { Collapsed(false); } for (TThrusters::iterator it=m_vecThrusters.begin(); it!=m_vecThrusters.end(); ++it) { (*it)->groundContact = false; } }
//------------------------------------------------------------------------ void CVehicleMovementStdBoat::Update(const float deltaTime) { CVehicleMovementBase::Update(deltaTime); SetAnimationSpeed(eVMA_Engine, abs(m_rpmScaleSgn)); if (m_inWater) { SetSoundParam(eSID_Run, "slip", 0.2f*abs(m_localSpeed.x)); } #if ENABLE_VEHICLE_DEBUG if (IsProfilingMovement() && g_pGameCVars->v_profileMovement != 2) { IEntity* pEntity = m_pVehicle->GetEntity(); const Matrix34& wTM = pEntity->GetWorldTM(); Matrix34 wTMInv = wTM.GetInvertedFast(); const SVehiclePhysicsStatus* physStatus = &m_physStatus[k_mainThread]; Vec3 localW = physStatus->q * physStatus->w; float speed = physStatus->v.len2() > 0.001f ? physStatus->v.len() : 0.f; float speedRatio = min(1.f, speed/(m_maxSpeed*m_factorMaxSpeed)); float absPedal = abs(m_movementAction.power); float absSteer = abs(m_movementAction.rotateYaw); static const float fSubmergedMin = 0.01f; static const float fWaterLevelMaxDiff = 0.15f; // max allowed height difference between propeller center and water level Vec3 worldPropPos = wTM * m_pushOffset; float waterLevelWorld = gEnv->p3DEngine->GetWaterLevel( &worldPropPos ); float fWaterLevelDiff = worldPropPos.z - waterLevelWorld; // wave stuff float waveFreq = 1.f; waveFreq += 3.f*speedRatio; float kx = m_waveIdleStrength.x*(m_waveRandomMult+0.3f) * (1.f-speedRatio + m_waveSpeedMult*speedRatio); float ky = m_waveIdleStrength.y * (1.f - 0.5f*absPedal - 0.5f*absSteer); Vec3 waveLoc = m_massOffset; waveLoc.y += speedRatio*min(0.f, m_pushOffset.y-m_massOffset.y); waveLoc = wTM * waveLoc; IRenderer* pRenderer = gEnv->pRenderer; static float color[4] = {1,1,1,1}; float colorRed[4] = {1,0,0,1}; float colorGreen[4] = {0,1,0,1}; float y=50.f, step1=15.f, step2=20.f, size1=1.3f, size2=1.5f; pRenderer->Draw2dLabel(5.0f, y, size2, color, false, "Boat movement"); pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "Speed: %.1f (%.1f km/h)", speed, speed*3.6f); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "LocalW.z norm: %.2f", abs(localW.z)/m_turnRateMax); if (m_velLift > 0.f) { pRenderer->Draw2dLabel(5.0f, y+=step2, size1, m_lifted ? colorGreen : color, false, m_lifted ? "Lifted" : "not lifted"); //pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "Impulse lift: %.0f", liftImp.impulse.len()); } pRenderer->Draw2dLabel(5.0f, y+=step1, size1, physStatus->submergedFraction > fSubmergedMin ? color : colorRed, false, "Submerged: %.2f", physStatus->submergedFraction); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, fWaterLevelDiff < fWaterLevelMaxDiff ? color : colorRed, false, "WaterLevel: %.2f (max: %.2f)", fWaterLevelDiff, fWaterLevelMaxDiff); pRenderer->Draw2dLabel(5.0f, y+=step2, size2, color, false, "Driver input"); pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "power: %.2f", m_movementAction.power); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "steer: %.2f", m_movementAction.rotateYaw); pRenderer->Draw2dLabel(5.0f, y+=step2, size2, color, false, "Propelling"); //pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "turnAccel (norm/real): %.2f / %.2f", turnAccelNorm, turnAccel); //pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Impulse acc: %.0f", linearImp.impulse.len()); //pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Impulse steer/damp: %.0f", angularImp.angImpulse.len()); //pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Impulse corner: %.0f", dampImp.impulse.len()); pRenderer->Draw2dLabel(5.0f, y+=step2, size2, color, false, "Waves"); pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "timer: %.1f", m_waveTimer); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "frequency: %.2f", waveFreq); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "random: %.2f", m_waveRandomMult); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "kX: %.2f", kx); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "kY: %.2f", ky); if (Boosting()) pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Boost: %.2f", m_boostCounter); IRenderAuxGeom* pGeom = pRenderer->GetIRenderAuxGeom(); ColorB colorB(0,255,0,255); pRenderer->DrawLabel(worldPropPos, 1.3f, "WL: %.2f", waterLevelWorld); pGeom->DrawSphere(worldPropPos, 0.15f, colorB); pGeom->DrawSphere(waveLoc, 0.25f, colorB); pGeom->DrawLine(waveLoc, colorB, waveLoc+Vec3(0,0,2), colorB); // impulses //DrawImpulse(linearImp, Vec3(0,0,1), 3.f/deltaTime, ColorB(255,0,0,255)); //DrawImpulse(angularImp, Vec3(0,0,1), 2.f/deltaTime, ColorB(128,0,0,255)); //DrawImpulse(liftImp, Vec3(0,0,6), 2.f/deltaTime, ColorB(0,0,255,255)); } #endif }
//------------------------------------------------------------------------ void CVehicleMovementStdBoat::UpdateSurfaceEffects(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); if (0 == g_pGameCVars->v_pa_surface) { ResetParticles(); return; } IEntity* pEntity = m_pVehicle->GetEntity(); const Matrix34& worldTM = pEntity->GetWorldTM(); float distSq = worldTM.GetTranslation().GetSquaredDistance(gEnv->pRenderer->GetCamera().GetPosition()); if (distSq > sqr(300.f) || (distSq > sqr(50.f) && !m_pVehicle->GetGameObject()->IsProbablyVisible())) return; Matrix34 worldTMInv = worldTM.GetInverted(); const SVehicleStatus& status = m_pVehicle->GetStatus(); float velDot = status.vel * worldTM.GetColumn1(); float powerNorm = min(abs(m_movementAction.power), 1.f); SEnvironmentParticles* envParams = m_pPaParams->GetEnvironmentParticles(); SEnvParticleStatus::TEnvEmitters::iterator end = m_paStats.envStats.emitters.end(); for (SEnvParticleStatus::TEnvEmitters::iterator emitterIt = m_paStats.envStats.emitters.begin(); emitterIt!=end; ++emitterIt) { if (emitterIt->layer < 0) { assert(0); continue; } const SEnvironmentLayer& layer = envParams->GetLayer(emitterIt->layer); SEntitySlotInfo info; info.pParticleEmitter = 0; pEntity->GetSlotInfo(emitterIt->slot, info); float countScale = 1.f; float sizeScale = 1.f; float speedScale = 1.f; float speed = 0.f; // check if helper position is beneath water level Vec3 emitterWorldPos = worldTM * emitterIt->quatT.t; float waterLevel = gEnv->p3DEngine->GetWaterLevel(&emitterWorldPos); int matId = 0; if (emitterWorldPos.z <= waterLevel+0.1f && m_physStatus[k_mainThread].submergedFraction<0.999f) { matId = gEnv->pPhysicalWorld->GetWaterMat(); speed = status.speed; bool spray = !strcmp(layer.GetName(), "spray"); if (spray) { // slip based speed -= abs(velDot); } GetParticleScale(layer, speed, powerNorm, countScale, sizeScale, speedScale); } else { countScale = 0.f; } if (matId && matId != emitterIt->matId) { // change effect IParticleEffect* pEff = 0; const char* effect = GetEffectByIndex( matId, layer.GetName() ); if (effect && (pEff = gEnv->pParticleManager->FindEffect(effect))) { #if ENABLE_VEHICLE_DEBUG if (DebugParticles()) CryLog("%s changes water sfx to %s (slot %i)", pEntity->GetName(), effect, emitterIt->slot); #endif if (info.pParticleEmitter) { info.pParticleEmitter->Activate(false); pEntity->FreeSlot(emitterIt->slot); } emitterIt->slot = pEntity->LoadParticleEmitter(emitterIt->slot, pEff); if (emitterIt->slot != -1) pEntity->SetSlotLocalTM(emitterIt->slot, Matrix34(emitterIt->quatT)); info.pParticleEmitter = 0; pEntity->GetSlotInfo(emitterIt->slot, info); } else countScale = 0.f; } if (matId) emitterIt->matId = matId; if (info.pParticleEmitter) { SpawnParams sp; sp.fSizeScale = sizeScale; sp.fCountScale = countScale; sp.fSpeedScale = speedScale; info.pParticleEmitter->SetSpawnParams(sp); if (layer.alignToWater && countScale > 0.f) { Vec3 worldPos(emitterWorldPos.x, emitterWorldPos.y, waterLevel+0.05f); Matrix34 localTM(emitterIt->quatT); localTM.SetTranslation(worldTMInv * worldPos); pEntity->SetSlotLocalTM(emitterIt->slot, localTM); } } #if ENABLE_VEHICLE_DEBUG if (DebugParticles() && m_pVehicle->IsPlayerDriving()) { float color[] = {1,1,1,1}; ColorB red(255,0,0,255); IRenderAuxGeom* pAuxGeom = gEnv->pRenderer->GetIRenderAuxGeom(); const char* effect = info.pParticleEmitter ? info.pParticleEmitter->GetName() : ""; const Matrix34& slotTM = m_pEntity->GetSlotWorldTM(emitterIt->slot); Vec3 ppos = slotTM.GetTranslation(); pAuxGeom->DrawSphere(ppos, 0.2f, red); pAuxGeom->DrawCone(ppos, slotTM.GetColumn1(), 0.1f, 0.5f, red); gEnv->pRenderer->Draw2dLabel(50.f, (float)(400+10*emitterIt->slot), 1.2f, color, false, "<%s> water fx: slot %i [%s], speed %.1f, sizeScale %.2f, countScale %.2f (pos %.0f,%0.f,%0.f)", pEntity->GetName(), emitterIt->slot, effect, speed, sizeScale, countScale, ppos.x, ppos.y, ppos.z); } #endif } // generate water splashes Vec3 wakePos; if(m_pSplashPos) { wakePos = m_pSplashPos->GetWorldSpaceTranslation(); } else { wakePos = worldTM.GetTranslation(); } float wakeWaterLevel = gEnv->p3DEngine->GetWaterLevel(&wakePos); const Vec3& localW = m_localSpeed; if (localW.x >= 0.f) m_diving = false; if (!m_diving && localW.x < -0.03f && status.speed > 10.f && wakePos.z < m_lastWakePos.z && wakeWaterLevel+0.1f >= wakePos.z) { float speedRatio = min(1.f, status.speed/(m_maxSpeed*m_factorMaxSpeed)); m_diving = true; if (m_pWaveEffect) { if (IParticleEmitter* pEmitter = pEntity->GetParticleEmitter(m_wakeSlot)) { pEmitter->Activate(false); pEntity->FreeSlot(m_wakeSlot); m_wakeSlot = -1; } SpawnParams spawnParams; spawnParams.fSizeScale = spawnParams.fCountScale = 0.5f + 0.25f*speedRatio; spawnParams.fSizeScale += 0.4f*m_waveRandomMult; spawnParams.fCountScale += cry_random(0.0f, 0.4f); m_wakeSlot = pEntity->LoadParticleEmitter(m_wakeSlot, m_pWaveEffect, &spawnParams); } // handle splash sound ExecuteTrigger(eSID_Splash); SetSoundParam(eSID_Splash, "intensity", 0.2f*speedRatio + 0.5f*m_waveRandomMult); if (m_rpmPitchDir == 0) { m_rpmPitchDir = -1; m_waveSoundPitch = 0.f; m_waveSoundAmount = 0.02f + m_waveRandomMult*0.08f; } } if (m_wakeSlot != -1) { // update emitter local pos to short above waterlevel Matrix34 tm; if(m_pSplashPos) m_pSplashPos->GetVehicleTM(tm); else tm.SetIdentity(); Vec3 pos = tm.GetTranslation(); pos.z = worldTMInv.TransformPoint(Vec3(wakePos.x,wakePos.y,wakeWaterLevel)).z + 0.2f; tm.SetTranslation(pos); pEntity->SetSlotLocalTM(m_wakeSlot, tm); #if ENABLE_VEHICLE_DEBUG if (IsProfilingMovement()) { Vec3 wPos = worldTM * tm.GetTranslation(); ColorB col(128, 128, 0, 200); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(wPos, 0.4f, col); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(wPos, col, wPos+Vec3(0,0,1.5f), col); } #endif } m_lastWakePos = wakePos; }
void CVehicleMovementAerodynamic::DrawLine(Vec3 *_pvPoint1,Vec3 *_pvPoint2,ColorF _Color) { if (IsProfilingMovement()) gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(*_pvPoint1,_Color,*_pvPoint2,_Color); }