You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.1 KiB
273 lines
8.1 KiB
2 months ago
|
//
|
||
|
// Copyright (c) 2010-2011 Matthew Jack and Doug Binks
|
||
|
//
|
||
|
// This software is provided 'as-is', without any express or implied
|
||
|
// warranty. In no event will the authors be held liable for any damages
|
||
|
// arising from the use of this software.
|
||
|
// Permission is granted to anyone to use this software for any purpose,
|
||
|
// including commercial applications, and to alter it and redistribute it
|
||
|
// freely, subject to the following restrictions:
|
||
|
// 1. The origin of this software must not be misrepresented; you must not
|
||
|
// claim that you wrote the original software. If you use this software
|
||
|
// in a product, an acknowledgment in the product documentation would be
|
||
|
// appreciated but is not required.
|
||
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
||
|
// misrepresented as being the original software.
|
||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||
|
|
||
|
#include "IPhysicsManager.h"
|
||
|
#include "IGameManager.h"
|
||
|
#include "IGameObject.h"
|
||
|
#include "IObjectUtils.h"
|
||
|
#include "ICameraControl.h"
|
||
|
#include "GlobalParameters.h"
|
||
|
|
||
|
#include "../../Common/Math.inl"
|
||
|
#include "../../RuntimeObjectSystem/ObjectInterfacePerModule.h"
|
||
|
#include "../../RuntimeCompiler/IFileChangeNotifier.h"
|
||
|
#include "../../Systems/SystemTable.h"
|
||
|
#include "../../Systems/IEntitySystem.h"
|
||
|
#include "../../Systems/IAssetSystem.h"
|
||
|
#include "../../Systems/ILogSystem.h"
|
||
|
#include "../../RuntimeObjectSystem/ISimpleSerializer.h"
|
||
|
#include "../../Systems/IGUISystem.h"
|
||
|
#include "../../Systems/IGame.h"
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <limits>
|
||
|
#include <algorithm>
|
||
|
|
||
|
|
||
|
class PhysicsManager: public IPhysicsManager, public IGameEventListener
|
||
|
{
|
||
|
// We have two sets of typedefs here, one for fast access during runtime, and another
|
||
|
// that is used for safe storage during serialization
|
||
|
typedef std::vector<IGameObject*> TGameObjects;
|
||
|
typedef std::vector<ObjectId> TGameObjectIds;
|
||
|
|
||
|
public:
|
||
|
PhysicsManager()
|
||
|
{
|
||
|
m_Objects.resize(EGT_COUNT);
|
||
|
}
|
||
|
|
||
|
virtual ~PhysicsManager()
|
||
|
{
|
||
|
((IGameManager*)IObjectUtils::GetUniqueInterface( "GameManager", IID_IGAMEMANAGER ))->RemoveListener(this);
|
||
|
}
|
||
|
|
||
|
|
||
|
// IObject
|
||
|
|
||
|
virtual void Serialize(ISimpleSerializer *pSerializer)
|
||
|
{
|
||
|
AU_ASSERT(pSerializer);
|
||
|
SERIALIZE(m_fWorldCenteringDist);
|
||
|
SerializeObjectsList( pSerializer );
|
||
|
}
|
||
|
|
||
|
virtual void Init( bool isFirstInit )
|
||
|
{
|
||
|
IGameManager* pGameManager = (IGameManager*)IObjectUtils::GetUniqueInterface( "GameManager", IID_IGAMEMANAGER );
|
||
|
pGameManager->AddListener(this);
|
||
|
|
||
|
float height, width;
|
||
|
PerModuleInterface::g_pSystemTable->pGame->GetWindowSize( width, height );
|
||
|
|
||
|
// Slightly reduce dimensions so object never cross window edges (since their position is in the center of object
|
||
|
width -= 50;
|
||
|
height -= 50;
|
||
|
|
||
|
m_fWorldCenteringDist.SetX( width * width * 0.25f );
|
||
|
m_fWorldCenteringDist.SetZ( height * height * 0.25f );
|
||
|
}
|
||
|
|
||
|
// ~IObject
|
||
|
|
||
|
// IGameEventListener
|
||
|
|
||
|
virtual void OnGameReset()
|
||
|
{
|
||
|
for (int i=0; i<EGT_COUNT; ++i)
|
||
|
{
|
||
|
m_Objects[i].clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual void OnGameObjectCreated( IGameObject* pGameObject )
|
||
|
{
|
||
|
m_Objects[pGameObject->GetGameTeam()].push_back( pGameObject );
|
||
|
}
|
||
|
|
||
|
virtual void OnGameObjectAboutToDestroy( IGameObject* pGameObject )
|
||
|
{
|
||
|
TGameObjects& data = m_Objects[pGameObject->GetGameTeam()];
|
||
|
TGameObjects::iterator it = std::find(data.begin(), data.end(), pGameObject);
|
||
|
if (it != data.end())
|
||
|
{
|
||
|
data.erase(it);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ~IGameEventListener
|
||
|
|
||
|
// IPhysicsManager
|
||
|
|
||
|
virtual void RequestPositionUpdate( IGameObject* pGameObject, const AUVec3f& desiredPosition, float frameDelta )
|
||
|
{
|
||
|
AUVec3f pos = desiredPosition;
|
||
|
|
||
|
ApplyGameAreaRepulsionField( pGameObject, pos, frameDelta );
|
||
|
ApplyTeamRepulsionFields( pGameObject, pos, frameDelta );
|
||
|
|
||
|
CheckForCollisions( pGameObject, pos, frameDelta );
|
||
|
|
||
|
pGameObject->GetEntity()->SetPosition(pos);
|
||
|
}
|
||
|
|
||
|
virtual bool IsHeadingToGameBounds( const AUVec3f& position, const AUVec3f& velocity ) const
|
||
|
{
|
||
|
float edgeProportion = (position.x * position.x) / m_fWorldCenteringDist.x +
|
||
|
(position.z * position.z) / m_fWorldCenteringDist.z;
|
||
|
return ( edgeProportion > 0.8f && position.Dot(velocity) > cos( M_PI_4 ) );
|
||
|
}
|
||
|
|
||
|
// ~IPhysicsManager
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
void ApplyGameAreaRepulsionField( IGameObject* pGameObject, AUVec3f& desiredPosition, float frameDelta )
|
||
|
{
|
||
|
float edgeProportion = (desiredPosition.x * desiredPosition.x) / m_fWorldCenteringDist.x +
|
||
|
(desiredPosition.z * desiredPosition.z) / m_fWorldCenteringDist.z;
|
||
|
float forceStart = 0.9f;
|
||
|
if ( edgeProportion > forceStart )
|
||
|
{
|
||
|
float centeringMagnitude = (edgeProportion - forceStart) / (1.0f - forceStart);
|
||
|
AUVec3f dir = (-desiredPosition).GetNormalised(); // Towards center
|
||
|
desiredPosition += dir * pGameObject->GetMaxSpeed() * frameDelta * centeringMagnitude;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ApplyTeamRepulsionFields( IGameObject* pGameObject, AUVec3f& desiredPosition, float frameDelta )
|
||
|
{
|
||
|
const AUVec3f& refPos = pGameObject->GetEntity()->GetPosition();
|
||
|
const float refDist = pGameObject->GetCollisionRadius();
|
||
|
const float forceStartMultiplier = 1.5f;
|
||
|
|
||
|
TGameObjects& data = m_Objects[pGameObject->GetGameTeam()];
|
||
|
TGameObjects::iterator it = data.begin();
|
||
|
TGameObjects::iterator itEnd = data.end();
|
||
|
while (it != itEnd)
|
||
|
{
|
||
|
IGameObject* pTeamObject = *it;
|
||
|
if (pTeamObject != pGameObject)
|
||
|
{
|
||
|
const AUVec3f& teamObjectPos = pTeamObject->GetEntity()->GetPosition();
|
||
|
const float minAllowedDist = refDist + pTeamObject->GetCollisionRadius();
|
||
|
const float distSqr = (refPos - teamObjectPos).MagnitudeSqr();
|
||
|
const float forceStart = minAllowedDist * forceStartMultiplier;
|
||
|
if ( distSqr < forceStart * forceStart )
|
||
|
{
|
||
|
float repulsionMagnitude = (forceStart - sqrt(distSqr)) / ( forceStart * ( 1.0f - 1.0f / forceStartMultiplier ) );
|
||
|
AUVec3f dir = (refPos - teamObjectPos).GetNormalised();
|
||
|
desiredPosition += dir * pGameObject->GetMaxSpeed() * frameDelta * repulsionMagnitude;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
++it;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CheckForCollisions( IGameObject* pGameObject, AUVec3f& desiredPosition, float frameDelta )
|
||
|
{
|
||
|
const AUVec3f& refPos = pGameObject->GetEntity()->GetPosition();
|
||
|
const float refDist = pGameObject->GetCollisionRadius();
|
||
|
|
||
|
for (int i=0; i<EGT_COUNT; ++i)
|
||
|
{
|
||
|
TGameObjects& data = m_Objects[i];
|
||
|
TGameObjects::iterator it = data.begin();
|
||
|
TGameObjects::iterator itEnd = data.end();
|
||
|
while (it != itEnd)
|
||
|
{
|
||
|
IGameObject* pTestObject = *it;
|
||
|
if (pTestObject != pGameObject)
|
||
|
{
|
||
|
const AUVec3f& testObjectPos = pTestObject->GetEntity()->GetPosition();
|
||
|
float distSqr = (refPos - testObjectPos).MagnitudeSqr();
|
||
|
float minAllowedDist = refDist + pTestObject->GetCollisionRadius();
|
||
|
if ( (distSqr <= minAllowedDist * minAllowedDist) && (pGameObject->GetGameTeam() != pTestObject->GetGameTeam()) )
|
||
|
{
|
||
|
// Set desired position to edge of collision radii
|
||
|
AUVec3f dir = (refPos - testObjectPos).GetNormalised();
|
||
|
desiredPosition = testObjectPos + dir * minAllowedDist;
|
||
|
|
||
|
pTestObject->OnCollision( pGameObject );
|
||
|
pGameObject->OnCollision( pTestObject );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
++it;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SerializeObjectsList( ISimpleSerializer *pSerializer )
|
||
|
{
|
||
|
std::vector<TGameObjectIds> m_ObjectIds;
|
||
|
|
||
|
if ( !pSerializer->IsLoading() )
|
||
|
{
|
||
|
// Create a collection of ObjectIds that matches m_Objects pointer collection
|
||
|
|
||
|
m_ObjectIds.resize(EGT_COUNT);
|
||
|
for (int i=0; i<EGT_COUNT; ++i)
|
||
|
{
|
||
|
size_t count = m_Objects[i].size();
|
||
|
m_ObjectIds[i].resize( count );
|
||
|
|
||
|
for (size_t j=0; j<count; ++j)
|
||
|
{
|
||
|
m_ObjectIds[i][j] = m_Objects[i][j]->GetObjectId();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SERIALIZE(m_ObjectIds);
|
||
|
|
||
|
if ( pSerializer->IsLoading() )
|
||
|
{
|
||
|
// Rebuild m_objects pointer collection
|
||
|
|
||
|
for (int i=0; i<EGT_COUNT; ++i)
|
||
|
{
|
||
|
size_t count = m_ObjectIds[i].size();
|
||
|
m_Objects[i].clear();
|
||
|
m_Objects[i].resize( count );
|
||
|
|
||
|
for (size_t j=0; j<count; ++j)
|
||
|
{
|
||
|
IGameObject* pGameObject = 0;
|
||
|
IObjectUtils::GetObject( &pGameObject, m_ObjectIds[i][j] );
|
||
|
|
||
|
m_Objects[i][j] = pGameObject;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Private Members
|
||
|
|
||
|
std::vector<TGameObjects> m_Objects; // set of gameobjects separated by team (this is handy for potential field calculations)
|
||
|
AUVec3f m_fWorldCenteringDist;
|
||
|
};
|
||
|
|
||
|
REGISTERCLASS(PhysicsManager);
|
||
|
|
||
|
|
||
|
|
||
|
|