Пример #1
0
void CDeflectorShield::ProcessProjectile(CProjectile* pProjectile, Vec3 hitPosition, Vec3 hitNormal, Vec3 hitDirection)
{
	if (CanProjectilePassThroughShield(pProjectile))
	{
		return;
	}

	const Matrix34& intoWorldTransform = GetEntity()->GetWorldTM();
	Matrix34 intoLocalTransform = intoWorldTransform.GetInvertedFast();

	SDeflectedEnergy deflectedEnergy;
	deflectedEnergy.m_delay = 0.0f;
	deflectedEnergy.m_localPosition = intoLocalTransform.TransformPoint(hitPosition);
	deflectedEnergy.m_localDirection = intoLocalTransform.TransformVector(-hitDirection);
	deflectedEnergy.m_damage = CLAMP(pProjectile->GetDamage(), m_minDamage, m_maxDamage);

	if (deflectedEnergy.m_localDirection.NormalizeSafe() > 0.0f)
	{
		m_deflectedEnergies.push_back(deflectedEnergy);
		GetGameObject()->EnableUpdateSlot(this, 0);
	}

	if (m_pDeflectedEffect)
	{
		m_pDeflectedEffect->Spawn(IParticleEffect::ParticleLoc(hitPosition, hitNormal));
	}

	pProjectile->Destroy();
}
//------------------------------------------------------------------------
int CScriptBind_Vehicle::GetHelperDir(IFunctionHandler* pH, const char* name, bool isInVehicleSpace)
{
	if (IVehicle *pVehicle = GetVehicle(pH))
	{
		if (IVehicleHelper* pHelper = pVehicle->GetHelper(name))
		{
			Matrix34 tm;
			if (isInVehicleSpace)
				pHelper->GetVehicleTM(tm);
			else
				tm = pHelper->GetLocalTM();
			
			return pH->EndFunction(tm.TransformVector(FORWARD_DIRECTION));
		}
	}

	return pH->EndFunction(Vec3(0.0f, 1.0f, 0.0f));
}
//------------------------------------------------------------------------
void CVehicleViewFirstPerson::Update(float frameTimeIn)
{
	// Use the physics frame time, but only if non zero!
	const float physFrameTime = static_cast<CVehicle*>(m_pVehicle)->GetPhysicsFrameTime();
	const float frameTime = (physFrameTime>0.f) ? min(physFrameTime,frameTimeIn) : frameTimeIn;

	CVehicleViewBase::Update(frameTime);

	if (m_frameSlot != -1 && m_pHelper)
	{
		Matrix34 tm;
		m_pHelper->GetVehicleTM(tm);
		tm = tm * m_invFrame;
		tm.SetTranslation(tm.GetTranslation() + tm.TransformVector(m_frameObjectOffset));
		m_pVehicle->GetEntity()->SetSlotLocalTM(m_frameSlot, tm);
	}        

	m_viewPosition = GetWorldPosGoal();
}
Пример #4
0
//---------------------------------------------------------------------
//This function is only executed on the server
void CC4Projectile::StickToStaticObject(EventPhysCollision *pCollision, IPhysicalEntity *pTarget)
{
	//Calculate new position and orientation
	Matrix34 mat;
	Vec3 pos = pCollision->pt+(pCollision->n*0.05f);
	mat.SetRotation33(Matrix33::CreateOrientation(-pCollision->n,GetEntity()->GetWorldTM().TransformVector(Vec3(0,0,1)),gf_PI));
	Vec3 newUpDir = mat.TransformVector(Vec3(0,0,1));
	pos += (newUpDir*-0.1f);
	mat.SetTranslation(pos+(newUpDir*-0.1f));
	GetEntity()->SetWorldTM(mat);

	GetGameObject()->SetAspectProfile(eEA_Physics, ePT_Static);

	pos = mat.GetTranslation();
	Quat rot = GetEntity()->GetWorldRotation();

	if(gEnv->bMultiplayer)
		GetGameObject()->InvokeRMI(CC4Projectile::ClSetPosition(),ProjectileStaticParams(pos,rot),eRMI_ToAllClients);
}
    bool CCoherentInputEventListener::TraceMouse( int& outX, int& outY, CCoherentViewListener*& pViewListener )
    {
        if ( gCoherentUISystem == nullptr )
        {
            return false;
        }

        CCamera& camera = gEnv->pSystem->GetViewCamera();
        int vpWidth = gEnv->pRenderer->GetWidth();
        int vpHeight = gEnv->pRenderer->GetHeight();
        float proj22 = 1.0f / cry_tanf( camera.GetFov() / 2.0f );
        float proj11 = proj22 / camera.GetProjRatio();
        float viewX = ( ( ( 2.0f * ( float )GetMouseX() ) / vpWidth ) - 1.0f ) / proj11;
        float viewY = ( ( ( -2.0f * ( float )GetMouseY() ) / vpHeight ) + 1.0f ) / proj22;
        Matrix34 invView = camera.GetMatrix();
        Vec3 dir = invView.TransformVector( Vec3( viewX, 1.0f, viewY ) ); // Z is up

        Vec3 origin = camera.GetPosition();

        return gCoherentUISystem->RaycastClosestViewListenersGeometry( origin, dir, outX, outY, pViewListener );
    }
//////////////////////////////////////////////////////////////////////////
// NOTE: This function must be thread-safe. Before adding stuff contact MarcoC.
void CVehicleMovementTank::ProcessMovement(const float deltaTime)
{ 
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_GAME );

	m_netActionSync.UpdateObject(this);

	CryAutoCriticalSection lk(m_lock);

	CVehicleMovementBase::ProcessMovement(deltaTime);

	if (!(m_actorId && m_isEnginePowered))
	{
		IPhysicalEntity* pPhysics = GetPhysics();

		if (m_latFriction != 1.3f)
			SetLatFriction(1.3f);

		if (m_axleFriction != m_axleFrictionMax)
			UpdateAxleFriction(0.f, false, deltaTime);

		m_action.bHandBrake = 1;
		m_action.pedal = 0;
		m_action.steer = 0;
		pPhysics->Action(&m_action, 1);
		return;
	}

	IPhysicalEntity* pPhysics = GetPhysics();
	MARK_UNUSED m_action.clutch;

	Matrix34 worldTM( m_PhysPos.q );
	worldTM.AddTranslation( m_PhysPos.pos );

	const Matrix34 invWTM = worldTM.GetInvertedFast();

	Vec3 localVel = invWTM.TransformVector(m_PhysDyn.v);
	Vec3 localW = invWTM.TransformVector(m_PhysDyn.w);
	float speed = m_PhysDyn.v.len();
	float speedRatio = min(1.f, speed/m_maxSpeed);

	float actionPedal = abs(m_movementAction.power) > 0.001f ? m_movementAction.power : 0.f;        

	// tank specific:
	// avoid steering input around 0.5 (ask Anton)
	float actionSteer = m_movementAction.rotateYaw;
	float absSteer = abs(actionSteer);
	float steerSpeed = (absSteer < 0.01f && abs(m_currSteer) > 0.01f) ? m_steerSpeedRelax : m_steerSpeed;

	if (steerSpeed == 0.f)
	{
		m_currSteer =	(float)sgn(actionSteer);
	}
	else
	{ 
		if (m_movementAction.isAI)
		{
			m_currSteer = actionSteer;
		}
		else
		{
			m_currSteer += min(abs(actionSteer-m_currSteer), deltaTime*steerSpeed) * sgn(actionSteer-m_currSteer);        
		}
	}
	Limit(m_currSteer, -m_steerLimit, m_steerLimit);  

	if (abs(m_currSteer) > 0.0001f) 
	{
		// if steering, apply full throttle to have enough turn power    
		actionPedal = (float)sgn(actionPedal);

		if (actionPedal == 0.f) 
		{
			// allow steering-on-teh-spot only above maxReverseSpeed (to avoid sudden reverse of controls)
			const float maxReverseSpeed = -1.5f;
			actionPedal = max(0.f, min(1.f, 1.f-(localVel.y/maxReverseSpeed)));

			// todo
			float steerLim = 0.8f;
			Limit(m_currSteer, -steerLim*m_steerLimit, steerLim*m_steerLimit);
		}
	}

	if (!pPhysics->GetStatus(&m_vehicleStatus))
		return;

	int currGear = m_vehicleStatus.iCurGear - 1; // indexing for convenience: -1,0,1,2,..

	UpdateAxleFriction(m_movementAction.power, true, deltaTime);
	UpdateSuspension(deltaTime);   	

	float absPedal = abs(actionPedal);  

	// pedal ramping   
	if (m_pedalSpeed == 0.f)
		m_currPedal = actionPedal;
	else
	{
		m_currPedal += deltaTime * m_pedalSpeed * sgn(actionPedal - m_currPedal);  
		m_currPedal = clamp_tpl(m_currPedal, -absPedal, absPedal);
	}

	// only apply pedal after threshold is exceeded
	if (currGear == 0 && fabs_tpl(m_currPedal) < m_pedalThreshold) 
		m_action.pedal = 0;
	else
		m_action.pedal = m_currPedal;

	// change pedal amount based on damages
	float damageMul = 0.0f;
	{
		if (m_movementAction.isAI)
		{
			damageMul = 1.0f - 0.30f * m_damage; 
			m_action.pedal *= damageMul;
		}
		else
		{
			// request from Sten: damage shouldn't affect reversing so much.
			float effectiveDamage = m_damage;
			if(m_action.pedal < -0.1f)
				effectiveDamage = 0.4f * m_damage;

			m_action.pedal *= GetWheelCondition();
			damageMul = 1.0f - 0.7f*effectiveDamage; 
			m_action.pedal *= damageMul;
		}
	}

	// reverse steering value for backward driving
	float effSteer = m_currSteer * sgn(actionPedal);   

	// update lateral friction  
	float latSlipMinGoal = 0.f;
	float latFricMinGoal = m_latFricMin;

	if (abs(effSteer) > 0.01f && !m_movementAction.brake)
	{
		latSlipMinGoal = m_latSlipMin;

		// use steering friction, but not when countersteering
		if (sgn(effSteer) != sgn(localW.z))
			latFricMinGoal = m_latFricMinSteer;
	}

	Interpolate(m_currentSlipMin, latSlipMinGoal, 3.f, deltaTime);   

	if (latFricMinGoal < m_currentFricMin)
		m_currentFricMin = latFricMinGoal;
	else
		Interpolate(m_currentFricMin, latFricMinGoal, 3.f, deltaTime);

	float fractionSpeed = min(1.f, max(0.f, m_avgLateralSlip-m_currentSlipMin) / (m_latSlipMax-m_currentSlipMin));
	float latFric = fractionSpeed * (m_latFricMax-m_currentFricMin) + m_currentFricMin;

	if ( m_movementAction.brake && m_movementAction.isAI )
	{
		// it is natural for ai, apply differnt friction value while handbreaking
		latFric = m_latFricMax;
	}

	if (latFric != m_latFriction)
	{ 
		SetLatFriction(latFric);    
	}      

	const static float maxSteer = gf_PI/4.f; // fix maxsteer, shouldn't change  
	m_action.steer = m_currSteer * maxSteer;  

	if (m_steeringImpulseMin > 0.f && m_wheelContactsLeft != 0 && m_wheelContactsRight != 0)
	{  
		const float maxW = 0.3f*gf_PI;
		float steer = abs(m_currSteer)>0.001f ? m_currSteer : 0.f;    
		float desired = steer * maxW; 
		float curr = -localW.z;
		float err = desired - curr; // err>0 means correction to right 
		Limit(err, -maxW, maxW);

		if (abs(err) > 0.01f)
		{ 
			float amount = m_steeringImpulseMin + speedRatio*(m_steeringImpulseMax-m_steeringImpulseMin);

			// bigger correction for relaxing
			if (desired == 0.f || (desired*curr>0 && abs(desired)<abs(curr))) 
				amount = m_steeringImpulseRelaxMin + speedRatio*(m_steeringImpulseRelaxMax-m_steeringImpulseRelaxMin);

			float corr = -err * amount * m_PhysDyn.mass * deltaTime;

			pe_action_impulse imp;
			imp.iApplyTime = 0;      
			imp.angImpulse = worldTM.GetColumn2() * corr;
			pPhysics->Action(&imp, THREAD_SAFE);
		}    
	}

	m_action.bHandBrake = (m_movementAction.brake) ? 1 : 0;	

	if (currGear > 0 && m_vehicleStatus.iCurGear < m_currentGear) 
	{
		// when shifted down, disengage clutch immediately to avoid power/speed dropdown
		m_action.clutch = 1.f;
	}

	pPhysics->Action(&m_action, 1);

	if (Boosting())  
		ApplyBoost(speed, 1.2f*m_maxSpeed*GetWheelCondition()*damageMul, m_boostStrength, deltaTime);  

	if (m_wheelContacts <= 1 && speed > 5.f)
	{
		ApplyAirDamp(DEG2RAD(20.f), DEG2RAD(10.f), deltaTime, THREAD_SAFE);
		UpdateGravity(-9.81f * 1.4f);
	}

	if (m_netActionSync.PublishActions( CNetworkMovementStdWheeled(this) ))
		CHANGED_NETWORK_STATE(m_pVehicle,  eEA_GameClientDynamic );
}
Пример #7
0
	virtual void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{
		if (!pActInfo->pEntity)
			return;

		if (event == eFE_Activate && IsPortActive(pActInfo, IN_ACTIVATE))
		{
			pe_action_impulse action;

			int ipart = GetPortInt( pActInfo, IN_PARTINDEX );
			if (ipart>0)
				action.ipart = ipart-1;

			IEntity* pEntity = pActInfo->pEntity;
			ECoordSys coordSys = (ECoordSys)GetPortInt( pActInfo, IN_COORDSYS );

			if (coordSys==CS_PARENT && !pEntity->GetParent())
				coordSys = CS_WORLD;

			// When a "zero point" is set in the node, the value is left undefined and physics assume it is the CM of the object.
			// but when the entity has a parent (is linked), then we have to use a real world coordinate for the point, because we have to apply the impulse to the highest entity 
			// on the hierarchy and physics will use the position of that entity instead of the position of the entity assigned to the node
			bool bHaveToUseTransformedZeroPoint = false;
			Vec3 transformedZeroPoint;
			Matrix34 transMat;

		  switch (coordSys)
			{
				case CS_WORLD:
				default:
				{
					transMat.SetIdentity();
					bHaveToUseTransformedZeroPoint = pEntity->GetParent()!=NULL;
					transformedZeroPoint = pEntity->GetWorldPos();
					break;
				}

				case CS_PARENT:
				{
					transMat = pEntity->GetParent()->GetWorldTM();
					bHaveToUseTransformedZeroPoint = pEntity->GetParent()->GetParent()!=NULL;
					transformedZeroPoint = pEntity->GetParent()->GetWorldPos();
					break;
				}

				case CS_LOCAL:
				{
					transMat = pEntity->GetWorldTM();
					bHaveToUseTransformedZeroPoint = pEntity->GetParent()!=NULL;
					transformedZeroPoint = pEntity->GetWorldPos();
					break;
				}
			}

			action.impulse = GetPortVec3( pActInfo, IN_IMPULSE );
			action.impulse = transMat.TransformVector( action.impulse );

			Vec3 angImpulse = GetPortVec3( pActInfo, IN_ANGIMPULSE );
			if (!angImpulse.IsZero())
				action.angImpulse = transMat.TransformVector( angImpulse );

			Vec3 pointApplication = GetPortVec3( pActInfo, IN_POINT );
			if (!pointApplication.IsZero())
				action.point = transMat.TransformPoint( pointApplication );
			else
			{
				if (bHaveToUseTransformedZeroPoint)
					action.point = transformedZeroPoint;
			}


			// the impulse has to be applied to the highest entity in the hierarchy. This comes from how physics manage linked entities.
			IEntity* pEntityImpulse = pEntity;
			while (pEntityImpulse->GetParent())
			{
				pEntityImpulse = pEntityImpulse->GetParent();
			}

			IPhysicalEntity * pPhysEntity = pEntityImpulse->GetPhysics();
			if (pPhysEntity)
				pPhysEntity->Action( &action );
		}
	}
Пример #8
0
	virtual void ProcessEvent(EFlowEvent event, SActivationInfo *pActInfo)
	{
		
		
		switch (event)
		{
		case eFE_Initialize:{
			pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID,true);
		}
		case eFE_Activate:
			{
				pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID,true);
			}
			break;
		
		case eFE_Update:
			{	
				//if (IsPortActive(pActInfo, PORT_IN_ENABLE))
				//{
					EntityId id = GetPortEntityId(pActInfo,EIP_EntityID);
					EntityId parentid = GetPortEntityId(pActInfo,EIP_ParentEntityID);
					//IGameObject *pGameObj = gEnv->pGameFramework->GetGameObject(id);
					IEntity * entity = gEnv->pEntitySystem->GetEntity(id);
					IEntity * parententity = gEnv->pEntitySystem->GetEntity(parentid);
					if(entity){
						Matrix34 trans; // = entity->GetWorldTM();
						trans.CreateIdentity();
						Matrix34 transparent;
						transparent.CreateIdentity(); 

						if (parententity){
							//quatParent = parententity->GetRotation();
							transparent = parententity->GetWorldTM();
							//CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn0().x,transparent.GetColumn0().y,transparent.GetColumn0().z);
							//CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn1().x,transparent.GetColumn1().y,transparent.GetColumn1().z);
							//CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn2().x,transparent.GetColumn2().y,transparent.GetColumn2().z);
							//CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn3().x,transparent.GetColumn3().y,transparent.GetColumn3().z);
							
						}
						
						Quat quat =  entity->GetRotation();
						quat.v = GetPortVec3( pActInfo, EIP_Rotation_XYZ);
						quat.w = GetPortFloat( pActInfo, EIP_Rotation_W);
						quat.NormalizeFast();
						

						Vec3 position = GetPortVec3(pActInfo, EIP_Position);
						
						trans.Set(Vec3(1,1,1),quat.GetNormalized(),position);
						//transparent.SetTranslation(Vec3(0,0,0));
						
						Vec3 positionlocal = transparent.TransformVector(position);

						trans = transparent * trans;

						//Matrix34 transresult ;
						//transresult.CreateIdentity();
						//transresult.Scale
						
						trans.SetTranslation(positionlocal+parententity->GetPos());

						if(trans.IsValid()){
							entity->SetWorldTM(trans);
						}
						else {
							//CryLogAlways("[%f,%f,%f]",trans.GetColumn0().x,trans.GetColumn0().y,trans.GetColumn0().z);
							//CryLogAlways("[%f,%f,%f]",trans.GetColumn1().x,trans.GetColumn1().y,trans.GetColumn1().z);
							//CryLogAlways("[%f,%f,%f]",trans.GetColumn2().x,trans.GetColumn2().y,trans.GetColumn2().z);
							//CryLogAlways("[%f,%f,%f]",trans.GetColumn3().x,trans.GetColumn3().y,trans.GetColumn3().z);
							//CryLogAlways("rot : [%f,%f,%f,%f]",trans.GetRo);
							//CryLogAlways("sca : [%f,%f,%f]",trans.GetColumn0().x,trans.GetColumn0().y,trans.GetColumn0().z);
						}
					}
					//CryLogAlways("%i",id);
				//}
			}
		}
	}
    bool CCoherentInputEventListener::OnInputEvent( const SInputEvent& event )
    {
        // Process special keys that toggle a function
        if ( event.deviceId == eDI_Keyboard && ( event.state & eIS_Released ) )
        {
            switch ( event.keyId )
            {
                case eKI_NP_0:
                    m_PlayerInputEnabled = !m_PlayerInputEnabled;
                    if (gEnv->pGame && gEnv->pGame->GetIGameFramework())
                    {
                        gEnv->pGame->GetIGameFramework()->GetIActionMapManager()->EnableActionMap( "player", m_PlayerInputEnabled );
                    }
                    return false;

                    // NP_1 starts raycasting so don't use it
                case eKI_NP_2:
                    m_DrawCoherentUI = !m_DrawCoherentUI;
                    return false;

                case eKI_NP_3:
                    m_DrawCursor = !m_DrawCursor;
                    ::ShowCursor( m_DrawCursor );

                    if ( gEnv && gEnv->pSystem && gEnv->pSystem->GetIHardwareMouse() )
                    {
                        if ( m_DrawCursor )
                        {
                            gEnv->pSystem->GetIHardwareMouse()->IncrementCounter();
                        }

                        else
                        {
                            gEnv->pSystem->GetIHardwareMouse()->DecrementCounter();
                        }
                    }

                    return false;

                case eKI_NP_4:
                    {
                        EntityId id = gEnv->pGame->GetIGameFramework()->GetClientActor()->GetGameObject()->GetWorldQuery()->GetLookAtEntityId();
                        IEntity* pEntityInFront = gEnv->pEntitySystem->GetEntity( id );

                        if ( pEntityInFront )
                        {
                            OutputDebugString( pEntityInFront->GetName() );
                        }
                    }

                    return false;

                case eKI_NP_5:
                    {
                        CCamera& cam = gEnv->pSystem->GetViewCamera();
                        int vpWidth = gEnv->pRenderer->GetWidth();
                        int vpHeight = gEnv->pRenderer->GetHeight();
                        float proj22 = 1.0f / cry_tanf( cam.GetFov() / 2.0f );
                        float proj11 = proj22 / cam.GetProjRatio();
                        float viewX = ( ( ( 2.0f * ( float )GetMouseX() ) / vpWidth ) - 1.0f ) / proj11;
                        float viewY = ( ( ( -2.0f * ( float )GetMouseY() ) / vpHeight ) + 1.0f ) / proj22;
                        Matrix34 invView = cam.GetMatrix();
                        Vec3 dir = invView.TransformVector( Vec3( viewX, 1.0f, viewY ) ); // Z is up
                        dir.Normalize();
                        dir *= 1000.0f;

                        Vec3 origin = cam.GetPosition();

                        ray_hit hit;
                        IPhysicalEntity* pSkipEnt = nullptr;
                        int hitCount = gEnv->pPhysicalWorld->RayWorldIntersection( origin, dir,
                                       ent_sleeping_rigid | ent_rigid,
                                       rwi_stop_at_pierceable | rwi_colltype_any, &hit, 1, pSkipEnt );

                        if ( hitCount > 0 )
                        {
                            IEntity* pEntity = gEnv->pEntitySystem->GetEntityFromPhysics( hit.pCollider );
                            OutputDebugString( pEntity->GetName() );
                        }
                    }

                    return false;

                case eKI_NP_6:
                    {
                        int dummyx, dummyy;
                        CCoherentViewListener* pViewListener;
                        TraceMouse( dummyx, dummyy, pViewListener );
                    }

                    return false;
            }
        }


        // Send input to Coherent UI only when the player input is disabled
        if ( m_PlayerInputEnabled )
        {
            return false;
        }

        if ( event.deviceId == eDI_Keyboard )
        {
            if ( !m_pLastFocusedViewListener || !m_pLastFocusedViewListener->GetView() )
            {
                return false;
            }

            Coherent::UI::KeyEventData keyEvent;

            if ( ToKeyEventData( event, keyEvent ) )
            {
                // Enter needs to be sent as KeyDown to work
                if ( keyEvent.KeyCode == 13 )
                {
                    keyEvent.Type = Coherent::UI::KeyEventData::KeyDown;
                    m_pLastFocusedViewListener->GetView()->KeyEvent( keyEvent );
                    keyEvent.Type = Coherent::UI::KeyEventData::Char;
                }

                m_pLastFocusedViewListener->GetView()->KeyEvent( keyEvent );
            }
        }

        else if ( event.deviceId == eDI_Mouse )
        {
            Coherent::UI::MouseEventData mouseEvent;
            int mouseX, mouseY;

            if ( ToMouseEvent( event, mouseEvent ) && TraceMouse( mouseX, mouseY, m_pLastFocusedViewListener ) )
            {
                mouseEvent.X = mouseX;
                mouseEvent.Y = mouseY;

                if ( m_pLastFocusedViewListener->GetView() )
                {
                    m_pLastFocusedViewListener->GetView()->MouseEvent( mouseEvent );
                }
            }
        }

        return false;
    }
Пример #10
0
//////////////////////////////////////////////////////////////////////////
// NOTE: This function must be thread-safe. Before adding stuff contact MarcoC.
void CVehicleMovementStdBoat::ProcessMovement(const float deltaTime)
{  
  FUNCTION_PROFILER( GetISystem(), PROFILE_GAME );

  static const float fWaterLevelMaxDiff = 0.15f; // max allowed height difference between propeller center and water level
  static const float fSubmergedMin = 0.01f;
  static const float fMinSpeedForTurn = 0.5f; // min speed so that turning becomes possible
  
  if (m_bNetSync)
    m_netActionSync.UpdateObject(this);

  CryAutoCriticalSection lk(m_lock);

  CVehicleMovementBase::ProcessMovement(deltaTime);

  IEntity* pEntity = m_pVehicle->GetEntity();
  IPhysicalEntity* pPhysics = pEntity->GetPhysics(); 
  SVehiclePhysicsStatus* physStatus = &m_physStatus[k_physicsThread];
  assert(pPhysics);

  float frameTime = min(deltaTime, 0.1f); 

  if (abs(m_movementAction.power) < 0.001f)
    m_movementAction.power = 0.f;
  if (abs(m_movementAction.rotateYaw) < 0.001f)
    m_movementAction.rotateYaw = 0.f;

  Matrix34 wTM( physStatus->q );
  wTM.AddTranslation( physStatus->pos );

  Matrix34 wTMInv = wTM.GetInvertedFast();
    
  Vec3 localVel = wTMInv.TransformVector( physStatus->v );
  Vec3 localW = wTMInv.TransformVector( physStatus->w );   

  const Vec3 xAxis = wTM.GetColumn0();
  const Vec3 yAxis = wTM.GetColumn1();
  const Vec3 zAxis = wTM.GetColumn2();
  
  // check if propeller is in water
  Vec3 worldPropPos = wTM * m_pushOffset;  
  float waterLevelWorld = gEnv->p3DEngine->GetWaterLevel( &worldPropPos );
  float fWaterLevelDiff = worldPropPos.z - waterLevelWorld;  
  
  bool submerged = physStatus->submergedFraction > fSubmergedMin;
  m_inWater = submerged && fWaterLevelDiff < fWaterLevelMaxDiff;
    
  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);
 
  // wave stuff 
  float waveFreq = 1.f;
  waveFreq += 3.f*speedRatio;

  float waveTimerPrev = m_waveTimer;
  m_waveTimer += frameTime*waveFreq;

  // new randomized amount for this oscillation
  if (m_waveTimer >= gf_PI && waveTimerPrev < gf_PI) 
    m_waveRandomMult = cry_random(0.0f, 1.0f);  
  
  if (m_waveTimer >= 2*gf_PI)  
    m_waveTimer -= 2*gf_PI;    

  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;

  bool visible = m_pVehicle->GetGameObject()->IsProbablyVisible();
  bool doWave = visible && submerged && physStatus->submergedFraction < 0.99f;
    
  if (doWave && !m_isEnginePowered)
    m_pVehicle->NeedsUpdate(IVehicle::eVUF_AwakePhysics);
  
  if (m_isEnginePowered || (visible && !m_pVehicle->IsProbablyDistant()))
  {
    if (doWave && (m_isEnginePowered || g_pGameCVars->v_rockBoats))
    { 
      pe_action_impulse waveImp;
      waveImp.angImpulse.x = Boosting() ? 0.f : sinf(m_waveTimer) * frameTime * m_Inertia.x * kx;
      
      if (isneg(waveImp.angImpulse.x))
        waveImp.angImpulse.x *= (1.f - min(1.f, 2.f*speedRatio)); // less amplitude for negative impulse      

      waveImp.angImpulse.y = sinf(m_waveTimer-0.5f*gf_PI) * frameTime * m_Inertia.y * ky;  
      waveImp.angImpulse.z = 0.f;
      waveImp.angImpulse = wTM.TransformVector(waveImp.angImpulse);
      waveImp.point = waveLoc;
      if (!m_movementAction.isAI)
	      pPhysics->Action(&waveImp, 1);      
    }
  }
  // ~wave stuff 

	if (!m_isEnginePowered)
		return;

  pe_action_impulse linearImp, angularImp, dampImp, liftImp; 
  float turnAccel = 0, turnAccelNorm = 0;

	if (m_inWater)
	{     
    // Lateral damping
    if (m_lateralDamping>0.f)
    {
    		pe_action_impulse impulse;
    		impulse.impulse = - physStatus->mass * xAxis * (localVel.x * (frameTime * m_lateralDamping)/(1.f + frameTime*m_lateralDamping));
    		pPhysics->Action(&impulse, 1);
    }
    // optional lifting (catamarans)
    if (m_velLift > 0.f)
    {
      if (localVel.y > m_velLift && !IsLifted())
        Lift(true);
      else if (localVel.y < m_velLift && IsLifted())
        Lift(false);
    }

    if (Boosting() && IsLifted())
    {
      // additional lift force      
      liftImp.impulse = Vec3(0,0,physStatus->mass*frameTime*(localVel.y/(m_velMax*m_factorMaxSpeed))*3.f);
      liftImp.point = wTM * m_massOffset;
      pPhysics->Action(&liftImp, 1);
    }
    
    // apply driving force         
    float a = m_movementAction.power;

    if (sgn(a)*sgn(localVel.y) > 0)
    {
      // reduce acceleration with increasing speed
      float ratio = (localVel.y > 0.f) ? localVel.y/(m_velMax*m_factorMaxSpeed) : -localVel.y/(m_velMaxReverse*m_factorMaxSpeed);      
      a = (ratio>1.f) ? 0.f : sgn(a)*min(abs(a), 1.f-((1.f-m_accelVelMax)*sqr(ratio))); 
    }
    
    if (a != 0)
    {
      if (sgn(a) * sgn(localVel.y) < 0) // "braking"
        a *= m_accelCoeff;    
      else
        a = max(a, -m_pedalLimitReverse);

      Vec3 pushDir(FORWARD_DIRECTION);                
      
      // apply force downwards a bit for more realistic response  
      if (a > 0)
        pushDir = Quat_tpl<float>::CreateRotationAA( DEG2RAD(m_pushTilt), Vec3(-1,0,0) ) * pushDir;

      pushDir = wTM.TransformVector( pushDir );  
      linearImp.impulse = pushDir * physStatus->mass * a * m_accel * m_factorAccel * frameTime;

      linearImp.point = m_pushOffset;
      linearImp.point.x = m_massOffset.x;
      linearImp.point = wTM * linearImp.point;
			pPhysics->Action(&linearImp, 1);
    } 
    
    float roll = (float)__fsel(zAxis.z - 0.2f, xAxis.z / (frameTime + frameTime*frameTime), 0.f);		// Roll damping (with a exp. time constant of 1 sec)

    // apply steering           
    if (m_movementAction.rotateYaw != 0)
    { 
      if (abs(localVel.y) < fMinSpeedForTurn){ // if forward speed too small, no turning possible
        turnAccel = 0; 
      }
      else 
      {
        int iDir = m_movementAction.power != 0.f ? sgn(m_movementAction.power) : sgn(localVel.y);
        turnAccelNorm = m_movementAction.rotateYaw * iDir * max(1.f, m_turnVelocityMult * speedRatio);    

        // steering and current w in same direction?
        int sgnSteerW = sgn(m_movementAction.rotateYaw) * iDir * sgn(-localW.z);

        if (sgnSteerW < 0)
        { 
          // "braking"
          turnAccelNorm *= m_turnAccelCoeff; 
        }
        else 
        {    
          // reduce turn vel towards max
          float maxRatio = 1.f - 0.15f*min(1.f, abs(localW.z)/m_turnRateMax);
          turnAccelNorm = sgn(turnAccelNorm) * min(abs(turnAccelNorm), maxRatio);
        }

        turnAccel = turnAccelNorm * m_turnAccel;
        //roll = 0.2f * turnAccel; // slight roll        
      }        
    }   

		// Use the centripetal acceleration to determine the amount of roll
		float centripetalAccel = clamp_tpl(speed * localW.z, -10.f, +10.f);
		roll -= (1.f - 2.f*fabsf(xAxis.z)) * m_rollAccel * centripetalAccel;

		// Always damp rotation!
		turnAccel += localW.z * m_turnDamping;
    
    if (turnAccel != 0)
    {
      Vec3& angImp = angularImp.angImpulse; 
      
      angImp.x = 0.f;
      angImp.y = roll * frameTime * m_Inertia.y;
      angImp.z = -turnAccel * frameTime * m_Inertia.z;      
      
      angImp = wTM.TransformVector( angImp );
      pPhysics->Action(&angularImp, 1);
    }   
    
    if (abs(localVel.x) > 0.01f)  
    { 
      // lateral force         
      Vec3& cornerForce = dampImp.impulse;
      
      cornerForce.x = -localVel.x * m_cornerForceCoeff * physStatus->mass * frameTime;
      cornerForce.y = 0.f;
      cornerForce.z = 0.f;
      
      if (m_cornerTilt != 0)
        cornerForce = Quat_tpl<float>::CreateRotationAA( sgn(localVel.x)*DEG2RAD(m_cornerTilt), Vec3(0,1,0) ) * cornerForce;

      dampImp.impulse = wTM.TransformVector(cornerForce);

      dampImp.point = m_cornerOffset;
      dampImp.point.x = m_massOffset.x;
      dampImp.point = wTM.TransformPoint( dampImp.point );
      pPhysics->Action(&dampImp, 1);         
    }  
  }

  EjectionTest(deltaTime);
  
	if (!m_pVehicle->GetStatus().doingNetPrediction)
	{
		if (m_bNetSync && m_netActionSync.PublishActions( CNetworkMovementStdBoat(this) ))
			CHANGED_NETWORK_STATE(m_pVehicle, CNetworkMovementStdBoat::CONTROLLED_ASPECT );
	}
}
//------------------------------------------------------------------------
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_statusDyn.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);

	Vec3 localW = worldTMInv.TransformVector(m_statusDyn.w);

	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_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 += 0.4f*Random();

			m_wakeSlot = pEntity->LoadParticleEmitter(m_wakeSlot, m_pWaveEffect, &spawnParams);
		}

		// handle splash sound
		PlaySound(eSID_Splash, 0.f, Vec3(0,5,1));
		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 CVehicleMovementStdBoat::Update(const float deltaTime)
{
	CVehicleMovementBase::Update(deltaTime);

	SetAnimationSpeed(eVMA_Engine, abs(m_rpmScaleSgn));

	if(m_inWater)
	{
		IEntity *pEntity = m_pVehicle->GetEntity();
		const Matrix34 &wTM = pEntity->GetWorldTM();
		Matrix34 wTMInv = wTM.GetInvertedFast();

		Vec3 localVel = wTMInv.TransformVector(m_statusDyn.v);

		SetSoundParam(eSID_Run, "slip", 0.2f*abs(localVel.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();

		Vec3 localVel = wTMInv.TransformVector(m_statusDyn.v);
		Vec3 localW = wTMInv.TransformVector(m_statusDyn.w);

		float speed = m_statusDyn.v.len2() > 0.001f ? m_statusDyn.v.len() : 0.f;
		float speedRatio = min(1.f, speed/m_maxSpeed);
		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, m_statusDyn.submergedFraction > fSubmergedMin ? color : colorRed, false, "Submerged: %.2f", m_statusDyn.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
}