|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
#include "plSimulationMgr.h"
|
|
|
|
|
|
|
|
#include "NxPhysics.h"
|
|
|
|
|
|
|
|
#include "hsTimer.h"
|
|
|
|
#include "plProfile.h"
|
|
|
|
#include "plPXPhysical.h"
|
|
|
|
#include "plPXPhysicalControllerCore.h"
|
|
|
|
#include "plPXConvert.h"
|
|
|
|
#include "plLOSDispatch.h"
|
|
|
|
#include "../plPhysical/plPhysicsSoundMgr.h"
|
|
|
|
#include "../plStatusLog/plStatusLog.h"
|
|
|
|
#include "../pnSceneObject/plSimulationInterface.h"
|
|
|
|
#include "../pnSceneObject/plCoordinateInterface.h"
|
|
|
|
#include "../pnNetCommon/plSDLTypes.h"
|
|
|
|
#include "../plMessage/plCollideMsg.h"
|
|
|
|
|
|
|
|
#include "../plModifier/plDetectorLog.h"
|
|
|
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
bool plSimulationMgr::fDisplayAwakeActors=false;
|
|
|
|
#endif //PLASMA_EXTERNAL_RELEASE
|
|
|
|
// This gets called by PhysX whenever a trigger gets penetrated. This is used
|
|
|
|
// for any Plasma detectors.
|
|
|
|
class SensorReport : public NxUserTriggerReport
|
|
|
|
{
|
|
|
|
virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status)
|
|
|
|
{
|
|
|
|
// Get our trigger physical. This should definitely have a plPXPhysical
|
|
|
|
plPXPhysical* triggerPhys = (plPXPhysical*)triggerShape.getActor().userData;
|
|
|
|
hsBool doReport = false;
|
|
|
|
|
|
|
|
// Get the triggerer. This may be an avatar, which doesn't have a
|
|
|
|
// plPXPhysical, so we have to extract the necessary info.
|
|
|
|
plKey otherKey = nil;
|
|
|
|
hsPoint3 otherPos = plPXConvert::Point(otherShape.getGlobalPosition());
|
|
|
|
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("-->%s %s (status=%x) other@(%f,%f,%f)",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit",status,otherPos.fX,otherPos.fY,otherPos.fZ);
|
|
|
|
|
|
|
|
plPXPhysical* otherPhys = (plPXPhysical*)otherShape.getActor().userData;
|
|
|
|
if (otherPhys)
|
|
|
|
{
|
|
|
|
otherKey = otherPhys->GetObjectKey();
|
|
|
|
doReport = triggerPhys->DoReportOn((plSimDefs::Group)otherPhys->GetGroup());
|
|
|
|
if (!doReport)
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s :failed group. US=%x OTHER=(%s)%x",triggerPhys->GetObjectKey()->GetName(),triggerPhys->GetGroup(),otherPhys->GetObjectKey()->GetName(),otherPhys->GetGroup());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool isController;
|
|
|
|
plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(otherShape.getActor(),&isController);
|
|
|
|
if (controller)
|
|
|
|
{
|
|
|
|
if (isController)
|
|
|
|
{
|
|
|
|
#ifdef PHYSX_ONLY_TRIGGER_FROM_KINEMATIC
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s : ignoring controller events.",triggerPhys->GetObjectKey()->GetName());
|
|
|
|
return;
|
|
|
|
#else // else if trigger on both controller and kinematic
|
|
|
|
// only suppress controller collision 'enters' when disabled but let 'exits' continue
|
|
|
|
// ...this is because there are detector regions that are on the edge on ladders that the exit gets missed.
|
|
|
|
if ( ( !controller->IsEnabled() /*&& (status & NX_TRIGGER_ON_ENTER)*/ ) || controller->IsKinematic() )
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s : controller is not enabled.",triggerPhys->GetObjectKey()->GetName());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif // PHYSX_ONLY_TRIGGER_FROM_KINEMATIC
|
|
|
|
}
|
|
|
|
#ifndef PHYSX_ONLY_TRIGGER_FROM_KINEMATIC // if triggering only kinematics, then all should trigger
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// only suppress kinematic collision 'enters' when disabled but let 'exits' continue
|
|
|
|
// ...this is because there are detector regions that are on the edge on ladders that the exit gets missed.
|
|
|
|
if ( !controller->IsKinematic() /*&& (status & NX_TRIGGER_ON_ENTER) */ )
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s : kinematic is not enabled.",triggerPhys->GetObjectKey()->GetName());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // PHYSX_ONLY_TRIGGER_FROM_KINEMATIC
|
|
|
|
otherKey = controller->GetOwner();
|
|
|
|
doReport = triggerPhys->DoReportOn(plSimDefs::kGroupAvatar);
|
|
|
|
if (plSimulationMgr::fExtraProfile )
|
|
|
|
{
|
|
|
|
if (!doReport)
|
|
|
|
{
|
|
|
|
DetectorLogRed("<--Kill collision %s :failed group. US=%x OTHER=(NotAvatar)",triggerPhys->GetObjectKey()->GetName(),triggerPhys->GetGroup());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsPoint3 avpos;
|
|
|
|
controller->GetPositionSim(avpos);
|
|
|
|
DetectorLogRed("-->Avatar at (%f,%f,%f)",avpos.fX,avpos.fY,avpos.fZ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doReport)
|
|
|
|
{
|
|
|
|
#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND
|
|
|
|
if ( triggerPhys->DoDetectorHullWorkaround() )
|
|
|
|
{
|
|
|
|
if (status & NX_TRIGGER_ON_ENTER && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) )
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit");
|
|
|
|
SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true);
|
|
|
|
}
|
|
|
|
else if (status & NX_TRIGGER_ON_ENTER)
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName());
|
|
|
|
}
|
|
|
|
if (status & NX_TRIGGER_ON_LEAVE && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) )
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit");
|
|
|
|
SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false);
|
|
|
|
}
|
|
|
|
else if (status & NX_TRIGGER_ON_LEAVE)
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName());
|
|
|
|
}
|
|
|
|
if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) )
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s :failed event(CH)",triggerPhys->GetObjectKey()->GetName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#endif // USE_PHYSX_CONVEXHULL_WORKAROUND
|
|
|
|
if (status & NX_TRIGGER_ON_ENTER)
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit");
|
|
|
|
SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true);
|
|
|
|
}
|
|
|
|
if (status & NX_TRIGGER_ON_LEAVE)
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit");
|
|
|
|
SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false);
|
|
|
|
}
|
|
|
|
if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) )
|
|
|
|
{
|
|
|
|
if (plSimulationMgr::fExtraProfile)
|
|
|
|
DetectorLogRed("<--Kill collision %s :failed event",triggerPhys->GetObjectKey()->GetName());
|
|
|
|
}
|
|
|
|
#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND
|
|
|
|
}
|
|
|
|
#endif // USE_PHYSX_CONVEXHULL_WORKAROUND
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendCollisionMsg(plKey receiver, plKey hitter, hsBool entering)
|
|
|
|
{
|
|
|
|
plCollideMsg* msg = TRACKED_NEW plCollideMsg;
|
|
|
|
msg->fOtherKey = hitter;
|
|
|
|
msg->fEntering = entering;
|
|
|
|
msg->AddReceiver(receiver);
|
|
|
|
// msg->Send();\
|
|
|
|
//placing message in a list to be fired off after sim step
|
|
|
|
plSimulationMgr::GetInstance()->AddCollisionMsg(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
} gSensorReport;
|
|
|
|
|
|
|
|
// This gets called by PhysX whenever two actor groups that are set to report
|
|
|
|
// have a collision. We enable this for when a dynamic collides with anything.
|
|
|
|
class ContactReport : public NxUserContactReport
|
|
|
|
{
|
|
|
|
virtual void onContactNotify(NxContactPair& pair, NxU32 events)
|
|
|
|
{
|
|
|
|
plPXPhysical* phys1 = (plPXPhysical*)pair.actors[0]->userData;
|
|
|
|
plPXPhysical* phys2 = (plPXPhysical*)pair.actors[1]->userData;
|
|
|
|
|
|
|
|
// Normally, these are always valid because the avatar (who doesn't have
|
|
|
|
// a physical) will push other physicals away before they actually touch
|
|
|
|
// his actor. However, if the avatar is warped to a new position he may
|
|
|
|
// collide with the object for a few frames. We just ignore it.
|
|
|
|
if (!phys1 || !phys2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
plSimulationMgr::GetInstance()->ConsiderSynch(phys1, phys2);
|
|
|
|
|
|
|
|
if (phys1->GetSoundGroup() && phys2->GetSoundGroup())
|
|
|
|
{
|
|
|
|
hsPoint3 contactPoint(0, 0, 0);
|
|
|
|
|
|
|
|
// Just grab the last contact point
|
|
|
|
NxContactStreamIterator i(pair.stream);
|
|
|
|
while (i.goNextPair())
|
|
|
|
{
|
|
|
|
while (i.goNextPatch())
|
|
|
|
{
|
|
|
|
const NxVec3& contactNormal = i.getPatchNormal();
|
|
|
|
while (i.goNextPoint())
|
|
|
|
{
|
|
|
|
contactPoint = plPXConvert::Point(i.getPoint());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plSimulationMgr::GetInstance()->fSoundMgr->AddContact(
|
|
|
|
phys1, phys2, contactPoint,
|
|
|
|
plPXConvert::Vector(pair.sumNormalForce));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} gContactReport;
|
|
|
|
|
|
|
|
// This directs any errors or warnings from PhysX to the simulation log.
|
|
|
|
class ErrorStream : public NxUserOutputStream
|
|
|
|
{
|
|
|
|
virtual void reportError(NxErrorCode e, const char* message, const char* file, int line)
|
|
|
|
{
|
|
|
|
const char* errorType = nil;
|
|
|
|
switch (e)
|
|
|
|
{
|
|
|
|
case NXE_INVALID_PARAMETER: errorType = "invalid parameter"; break;
|
|
|
|
case NXE_INVALID_OPERATION: errorType = "invalid operation"; break;
|
|
|
|
case NXE_OUT_OF_MEMORY: errorType = "out of memory"; break;
|
|
|
|
case NXE_DB_INFO: errorType = "info"; break;
|
|
|
|
case NXE_DB_WARNING: errorType = "warning"; break;
|
|
|
|
default: errorType = "unknown error";
|
|
|
|
}
|
|
|
|
|
|
|
|
plSimulationMgr::Log("%s(%d) : %s: %s", file, line, errorType, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual NxAssertResponse reportAssertViolation(const char* message, const char* file, int line)
|
|
|
|
{
|
|
|
|
plSimulationMgr::Log("access violation : %s (%s(%d))", message, file, line);
|
|
|
|
hsAssert(0, "PhysX assert, see simulation log for details");
|
|
|
|
return NX_AR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void print(const char* message)
|
|
|
|
{
|
|
|
|
plSimulationMgr::Log(message);
|
|
|
|
}
|
|
|
|
} gErrorStream;
|
|
|
|
|
|
|
|
// This class allows PhysX to use our heap manager
|
|
|
|
static class HeapAllocator : public NxUserAllocator {
|
|
|
|
public:
|
|
|
|
void * malloc (NxU32 size) {
|
|
|
|
return ALLOC(size);
|
|
|
|
}
|
|
|
|
void * mallocDEBUG (NxU32 size, const char * fileName, int line) {
|
|
|
|
return MemAlloc(size, 0, fileName, line);
|
|
|
|
}
|
|
|
|
void * realloc (void * memory, NxU32 size) {
|
|
|
|
return REALLOC(memory, size);
|
|
|
|
}
|
|
|
|
void free (void * memory) {
|
|
|
|
FREE(memory);
|
|
|
|
}
|
|
|
|
} gHeapAllocator;
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// DEFAULTS
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#define kDefaultMaxDelta 0.1 // if the step is greater than .1 seconds, clamp to that
|
|
|
|
#define kDefaultStepSize 1.f / 60.f // default simulation freqency is 60hz
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// CONSTRUCTION, INITIALIZATION, DESTRUCTION
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//
|
|
|
|
// Alloc all the sim timers here so they make a nice pretty display
|
|
|
|
//
|
|
|
|
//plProfile_CreateTimer( "ClearContacts", "Simulation", ClearContacts);
|
|
|
|
plProfile_CreateTimer( "Step", "Simulation", Step);
|
|
|
|
// plProfile_CreateTimer( " Broadphase", "Simulation", Broadphase);
|
|
|
|
plProfile_CreateCounter(" Awake", "Simulation", Awake);
|
|
|
|
plProfile_CreateCounter(" Contacts", "Simulation", Contacts);
|
|
|
|
plProfile_CreateCounter(" DynActors", "Simulation", DynActors);
|
|
|
|
plProfile_CreateCounter(" DynShapes", "Simulation", DynShapes);
|
|
|
|
plProfile_CreateCounter(" StaticShapes", "Simulation", StaticShapes);
|
|
|
|
plProfile_CreateCounter(" Actors", "Simulation", Actors);
|
|
|
|
plProfile_CreateCounter(" PhyScenes", "Simulation", Scenes);
|
|
|
|
|
|
|
|
// plProfile_CreateCounter(" Broadphase Rejected", "Simulation", BroadphaseReject);
|
|
|
|
// plProfile_CreateCounter(" Broadphase Accepted", "Simulation", BroadphaseAccept);
|
|
|
|
// plProfile_CreateCounter(" Impact", "Simulation", Impact);
|
|
|
|
// plProfile_CreateCounter(" Penetration", "Simulation", Penetration);
|
|
|
|
// plProfile_CreateTimer( "Narrowphase", "Simulation", Narrowphase);
|
|
|
|
// plProfile_CreateTimer( "ProcessInterpenetration", "Simulation", ProcessInterpenetration);
|
|
|
|
plProfile_CreateTimer( "LineOfSight", "Simulation", LineOfSight);
|
|
|
|
plProfile_CreateTimer( "ProcessSyncs", "Simulation", ProcessSyncs);
|
|
|
|
plProfile_CreateTimer( "UpdateContexts", "Simulation", UpdateContexts);
|
|
|
|
// plProfile_CreateCounter(" ContextUpdates", "Simulation", ContextUpdates);
|
|
|
|
plProfile_CreateCounter(" MaySendLocation", "Simulation", MaySendLocation);
|
|
|
|
plProfile_CreateCounter(" LocationsSent", "Simulation", LocationsSent);
|
|
|
|
plProfile_CreateTimer( " PhysicsUpdates","Simulation",PhysicsUpdates);
|
|
|
|
// plProfile_CreateTimer( "EntityCleanup", "Simulation", EntityCleanup);
|
|
|
|
plProfile_CreateCounter("SetTransforms Accepted", "Simulation", SetTransforms);
|
|
|
|
plProfile_CreateCounter("AnimatedPhysicals", "Simulation", AnimatedPhysicals);
|
|
|
|
plProfile_CreateCounter("AnimatedActivators", "Simulation", AnimatedActivators);
|
|
|
|
plProfile_CreateCounter("Controllers", "Simulation", Controllers);
|
|
|
|
// plProfile_CreateCounter("NumSteps", "Simulation", NumSteps);
|
|
|
|
plProfile_CreateCounter("StepLength", "Simulation", StepLen);
|
|
|
|
|
|
|
|
// declared at file scope so that both GetInstance and the destructor can access it.
|
|
|
|
static plSimulationMgr* gTheInstance = NULL;
|
|
|
|
bool plSimulationMgr::fExtraProfile = false;
|
|
|
|
bool plSimulationMgr::fSubworldOptimization = false;
|
|
|
|
bool plSimulationMgr::fDoClampingOnStep=true;
|
|
|
|
|
|
|
|
void plSimulationMgr::Init()
|
|
|
|
{
|
|
|
|
hsAssert(!gTheInstance, "Initializing the sim when it's already been done");
|
|
|
|
gTheInstance = TRACKED_NEW plSimulationMgr();
|
|
|
|
if (gTheInstance->InitSimulation())
|
|
|
|
{
|
|
|
|
gTheInstance->RegisterAs(kSimulationMgr_KEY);
|
|
|
|
gTheInstance->GetKey()->RefObject();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// There was an error when creating the PhysX simulation
|
|
|
|
// ...then get rid of the simulation instance
|
|
|
|
DEL(gTheInstance); // clean up the memory we allocated
|
|
|
|
gTheInstance = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// when the app is going away completely
|
|
|
|
void plSimulationMgr::Shutdown()
|
|
|
|
{
|
|
|
|
hsAssert(gTheInstance, "Simulation manager missing during shutdown.");
|
|
|
|
if (gTheInstance)
|
|
|
|
{
|
|
|
|
// UnRef to match our Ref in Init(). Unless something strange is
|
|
|
|
// going on, this should destroy the instance and set gTheInstance to nil.
|
|
|
|
// gTheInstance->GetKey()->UnRefObject();
|
|
|
|
gTheInstance->UnRegister(); // this will destroy the instance
|
|
|
|
gTheInstance = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plSimulationMgr* plSimulationMgr::GetInstance()
|
|
|
|
{
|
|
|
|
return gTheInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
plSimulationMgr::plSimulationMgr()
|
|
|
|
: fSuspended(true)
|
|
|
|
, fMaxDelta(kDefaultMaxDelta)
|
|
|
|
, fStepSize(kDefaultStepSize)
|
|
|
|
, fLOSDispatch(TRACKED_NEW plLOSDispatch())
|
|
|
|
, fSoundMgr(new plPhysicsSoundMgr)
|
|
|
|
, fLog(nil)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plSimulationMgr::InitSimulation()
|
|
|
|
{
|
|
|
|
fSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, &gHeapAllocator, &gErrorStream);
|
|
|
|
if (!fSDK)
|
|
|
|
return false; // client will handle this and ask user to install
|
|
|
|
|
|
|
|
fLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "Simulation.log", plStatusLog::kFilledBackground | plStatusLog::kAlignToTop);
|
|
|
|
fLog->AddLine("Initialized simulation mgr");
|
|
|
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
// If this is an internal build, enable the PhysX debugger
|
|
|
|
fSDK->getFoundationSDK().getRemoteDebugger()->connect("localhost", 5425);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ( !plPXConvert::Validate() )
|
|
|
|
{
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
hsMessageBox("Ageia's PhysX or Plasma offsets have changed, need to rewrite conversion code.","PhysX Error",MB_OK);
|
|
|
|
#endif
|
|
|
|
return false; // client will handle this and ask user to install
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plSimulationMgr::~plSimulationMgr()
|
|
|
|
{
|
|
|
|
fLOSDispatch->UnRef();
|
|
|
|
fLOSDispatch = nil;
|
|
|
|
|
|
|
|
delete fSoundMgr;
|
|
|
|
fSoundMgr = nil;
|
|
|
|
|
|
|
|
hsAssert(fScenes.empty(), "Unreleased scenes at shutdown");
|
|
|
|
|
|
|
|
if (fSDK)
|
|
|
|
fSDK->release();
|
|
|
|
|
|
|
|
delete fLog;
|
|
|
|
fLog = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
NxScene* plSimulationMgr::GetScene(plKey world)
|
|
|
|
{
|
|
|
|
if (!world)
|
|
|
|
world = GetKey();
|
|
|
|
|
|
|
|
NxScene* scene = fScenes[world];
|
|
|
|
|
|
|
|
if (!scene)
|
|
|
|
{
|
|
|
|
UInt32 maxSteps = (UInt32)hsCeil(fMaxDelta / fStepSize);
|
|
|
|
|
|
|
|
NxSceneDesc sceneDesc;
|
|
|
|
sceneDesc.gravity.set(0, 0, -32.174049f);
|
|
|
|
sceneDesc.userTriggerReport = &gSensorReport;
|
|
|
|
sceneDesc.userContactReport = &gContactReport;
|
|
|
|
sceneDesc.maxTimestep = fStepSize;
|
|
|
|
sceneDesc.maxIter = maxSteps;
|
|
|
|
scene = fSDK->createScene(sceneDesc);
|
|
|
|
|
|
|
|
// Most physicals use the default friction and restitution values, so we
|
|
|
|
// make them the default.
|
|
|
|
NxMaterial* mat = scene->getMaterialFromIndex(0);
|
|
|
|
float rest = mat->getRestitution();
|
|
|
|
float sfriction = mat->getStaticFriction();
|
|
|
|
float dfriction = mat->getDynamicFriction();
|
|
|
|
mat->setRestitution(0.5);
|
|
|
|
mat->setStaticFriction(0.5);
|
|
|
|
mat->setDynamicFriction(0.5);
|
|
|
|
|
|
|
|
// By default we just leave all the collision groups enabled, since
|
|
|
|
// PhysX already makes sure that things like statics and statics don't
|
|
|
|
// collide. However, we do make it so the avatar and dynamic blockers
|
|
|
|
// only block avatars and dynamics.
|
|
|
|
for (int i = 0; i < plSimDefs::kGroupMax; i++)
|
|
|
|
{
|
|
|
|
scene->setGroupCollisionFlag(i, plSimDefs::kGroupAvatarBlocker, false);
|
|
|
|
scene->setGroupCollisionFlag(i, plSimDefs::kGroupDynamicBlocker, false);
|
|
|
|
scene->setGroupCollisionFlag(i, plSimDefs::kGroupLOSOnly, false);
|
|
|
|
scene->setGroupCollisionFlag(plSimDefs::kGroupLOSOnly, i, false);
|
|
|
|
}
|
|
|
|
scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatar, false);
|
|
|
|
scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatarBlocker, true);
|
|
|
|
scene->setGroupCollisionFlag(plSimDefs::kGroupDynamic, plSimDefs::kGroupDynamicBlocker, true);
|
|
|
|
scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupStatic, true);
|
|
|
|
scene->setGroupCollisionFlag( plSimDefs::kGroupStatic, plSimDefs::kGroupAvatar, true);
|
|
|
|
scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupDynamic, true);
|
|
|
|
|
|
|
|
// The dynamics are in actor group 1, everything else is in 0. Request
|
|
|
|
// a callback for whenever a dynamic touches something.
|
|
|
|
scene->setActorGroupPairFlags(0, 1, NX_NOTIFY_ON_TOUCH);
|
|
|
|
scene->setActorGroupPairFlags(1, 1, NX_NOTIFY_ON_TOUCH);
|
|
|
|
|
|
|
|
fScenes[world] = scene;
|
|
|
|
}
|
|
|
|
|
|
|
|
return scene;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::ReleaseScene(plKey world)
|
|
|
|
{
|
|
|
|
if (!world)
|
|
|
|
world = GetKey();
|
|
|
|
|
|
|
|
SceneMap::iterator it = fScenes.find(world);
|
|
|
|
hsAssert(it != fScenes.end(), "Unknown scene");
|
|
|
|
if (it != fScenes.end())
|
|
|
|
{
|
|
|
|
NxScene* scene = it->second;
|
|
|
|
if (scene->getNbActors() == 0)
|
|
|
|
{
|
|
|
|
fSDK->releaseScene(*scene);
|
|
|
|
fScenes.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::ISendCollisionMsg(plKey receiver, plKey hitter, hsBool entering)
|
|
|
|
{
|
|
|
|
plCollideMsg* msg = TRACKED_NEW plCollideMsg;
|
|
|
|
msg->fOtherKey = hitter;
|
|
|
|
msg->fEntering = entering;
|
|
|
|
msg->AddReceiver(receiver);
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::AddCollisionMsg(plCollideMsg* msg)
|
|
|
|
{
|
|
|
|
fCollisionMessages.Append(msg);
|
|
|
|
}
|
|
|
|
void plSimulationMgr::IDispatchCollisionMessages()
|
|
|
|
{
|
|
|
|
if(fCollisionMessages.GetCount())
|
|
|
|
{
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
DetectorLog("--------------------------------------------------");
|
|
|
|
DetectorLog("Dispatching collision messages from last sim step");
|
|
|
|
#endif
|
|
|
|
for(int i=0; i<fCollisionMessages.GetCount();i++)
|
|
|
|
{
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
DetectorLog("%s was hit by %s. Sending an %s message",fCollisionMessages[i]->GetReceiver(0)->GetName(),
|
|
|
|
fCollisionMessages[i]->GetSender()?fCollisionMessages[i]->GetSender()->GetName():"An Avatar",
|
|
|
|
fCollisionMessages[i]->fEntering? "enter" : "exit");
|
|
|
|
#endif
|
|
|
|
fCollisionMessages[i]->Send();
|
|
|
|
}
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
DetectorLog("--------------------------------------------------");
|
|
|
|
#endif
|
|
|
|
fCollisionMessages.SetCount(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::UpdateDetectorsInScene(plKey world, plKey avatar, hsPoint3& pos, bool entering)
|
|
|
|
{
|
|
|
|
// search thru the actors in a scene looking for convex hull detectors and see if the avatar is inside it
|
|
|
|
// ... and then send appropiate collision message if needed
|
|
|
|
NxScene* scene = GetScene(world);
|
|
|
|
plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded());
|
|
|
|
const plCoordinateInterface* ci = avObj->GetCoordinateInterface();
|
|
|
|
hsPoint3 soPos = ci->GetWorldPos();
|
|
|
|
if (scene)
|
|
|
|
{
|
|
|
|
UInt32 numActors = scene->getNbActors();
|
|
|
|
NxActor** actors = scene->getActors();
|
|
|
|
|
|
|
|
for (int i = 0; i < numActors; i++)
|
|
|
|
{
|
|
|
|
plPXPhysical* physical = (plPXPhysical*)actors[i]->userData;
|
|
|
|
if (physical && physical->DoDetectorHullWorkaround())
|
|
|
|
{
|
|
|
|
if ( physical->IsObjectInsideHull(pos) )
|
|
|
|
{
|
|
|
|
physical->SetInsideConvexHull(entering);
|
|
|
|
// we are entering this world... say we entered this detector
|
|
|
|
ISendCollisionMsg(physical->GetObjectKey(), avatar, entering);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::UpdateAvatarInDetector(plKey world, plPXPhysical* detector)
|
|
|
|
{
|
|
|
|
// search thru the actors in a scene looking for avatars that might be in the newly enabled detector region
|
|
|
|
// ... and then send appropiate collision message if needed
|
|
|
|
if ( detector->DoDetectorHullWorkaround() )
|
|
|
|
{
|
|
|
|
NxScene* scene = GetScene(world);
|
|
|
|
if (scene)
|
|
|
|
{
|
|
|
|
UInt32 numActors = scene->getNbActors();
|
|
|
|
NxActor** actors = scene->getActors();
|
|
|
|
|
|
|
|
for (int i = 0; i < numActors; i++)
|
|
|
|
{
|
|
|
|
if ( actors[i]->userData == nil )
|
|
|
|
{
|
|
|
|
// we go a controller
|
|
|
|
bool isController;
|
|
|
|
plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(*actors[i],&isController);
|
|
|
|
if (controller && controller->IsEnabled())
|
|
|
|
{
|
|
|
|
plKey avatar = controller->GetOwner();
|
|
|
|
plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded());
|
|
|
|
const plCoordinateInterface* ci;
|
|
|
|
if ( avObj && ( ci = avObj->GetCoordinateInterface() ) )
|
|
|
|
{
|
|
|
|
if ( detector->IsObjectInsideHull(ci->GetWorldPos()) )
|
|
|
|
{
|
|
|
|
detector->SetInsideConvexHull(true);
|
|
|
|
// we are entering this world... say we entered this detector
|
|
|
|
ISendCollisionMsg(detector->GetObjectKey(), avatar, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::Advance(float delSecs)
|
|
|
|
{
|
|
|
|
if (fSuspended)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (delSecs > fMaxDelta)
|
|
|
|
{
|
|
|
|
if (fExtraProfile)
|
|
|
|
Log("Step clamped from %f to limit of %f", delSecs, fMaxDelta);
|
|
|
|
delSecs = fMaxDelta;
|
|
|
|
}
|
|
|
|
plProfile_IncCount(StepLen, (int)(delSecs*1000));
|
|
|
|
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELASE
|
|
|
|
UInt32 stepTime = hsTimer::GetPrecTickCount();
|
|
|
|
#endif
|
|
|
|
plProfile_BeginTiming(Step);
|
|
|
|
plPXPhysicalControllerCore::UpdatePrestep(delSecs);
|
|
|
|
plPXPhysicalControllerCore::UpdatePoststep( delSecs);
|
|
|
|
|
|
|
|
for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++)
|
|
|
|
{
|
|
|
|
NxScene* scene = it->second;
|
|
|
|
bool do_advance = true;
|
|
|
|
if (fSubworldOptimization)
|
|
|
|
{
|
|
|
|
plKey world = (plKey)it->first;
|
|
|
|
if (world == GetKey())
|
|
|
|
world = nil;
|
|
|
|
do_advance = plPXPhysicalControllerCore::AnyControllersInThisWorld(world);
|
|
|
|
}
|
|
|
|
if (do_advance)
|
|
|
|
{
|
|
|
|
scene->simulate(delSecs);
|
|
|
|
scene->flushStream();
|
|
|
|
scene->fetchResults(NX_RIGID_BODY_FINISHED, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
plPXPhysicalControllerCore::UpdatePostSimStep(delSecs);
|
|
|
|
|
|
|
|
//sending off and clearing the Collision Messages generated by scene->simulate
|
|
|
|
IDispatchCollisionMessages();
|
|
|
|
|
|
|
|
plProfile_EndTiming(Step);
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
if(plSimulationMgr::fDisplayAwakeActors)IDrawActiveActorList();
|
|
|
|
#endif
|
|
|
|
if (fExtraProfile)
|
|
|
|
{
|
|
|
|
int contacts = 0, dynActors = 0, dynShapes = 0, awake = 0, stShapes=0, actors=0, scenes=0, controllers=0 ;
|
|
|
|
for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++)
|
|
|
|
{
|
|
|
|
bool do_advance = true;
|
|
|
|
if (fSubworldOptimization)
|
|
|
|
{
|
|
|
|
plKey world = (plKey)it->first;
|
|
|
|
if (world == GetKey())
|
|
|
|
world = nil;
|
|
|
|
do_advance = plPXPhysicalControllerCore::AnyControllersInThisWorld(world);
|
|
|
|
}
|
|
|
|
if (do_advance)
|
|
|
|
{
|
|
|
|
NxScene* scene = it->second;
|
|
|
|
NxSceneStats stats;
|
|
|
|
scene->getStats(stats);
|
|
|
|
|
|
|
|
contacts += stats.numContacts;
|
|
|
|
dynActors += stats.numDynamicActors;
|
|
|
|
dynShapes += stats.numDynamicShapes;
|
|
|
|
awake += stats.numDynamicActorsInAwakeGroups;
|
|
|
|
stShapes += stats.numStaticShapes;
|
|
|
|
actors += stats.numActors;
|
|
|
|
scenes += 1;
|
|
|
|
controllers += plPXPhysicalControllerCore::NumControllers();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plProfile_IncCount(Awake, awake);
|
|
|
|
plProfile_IncCount(Contacts, contacts);
|
|
|
|
plProfile_IncCount(DynActors, dynActors);
|
|
|
|
plProfile_IncCount(DynShapes, dynShapes);
|
|
|
|
plProfile_IncCount(StaticShapes, stShapes);
|
|
|
|
plProfile_IncCount(Actors, actors);
|
|
|
|
plProfile_IncCount(Scenes, scenes);
|
|
|
|
plProfile_IncCount(Controllers, controllers);
|
|
|
|
}
|
|
|
|
|
|
|
|
plProfile_IncCount(AnimatedPhysicals, plPXPhysical::fNumberAnimatedPhysicals);
|
|
|
|
plProfile_IncCount(AnimatedActivators, plPXPhysical::fNumberAnimatedActivators);
|
|
|
|
|
|
|
|
fSoundMgr->Update();
|
|
|
|
|
|
|
|
plProfile_BeginTiming(ProcessSyncs);
|
|
|
|
IProcessSynchs();
|
|
|
|
plProfile_EndTiming(ProcessSyncs);
|
|
|
|
|
|
|
|
plProfile_BeginTiming(UpdateContexts);
|
|
|
|
ISendUpdates();
|
|
|
|
plProfile_EndTiming(UpdateContexts);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::ISendUpdates()
|
|
|
|
{
|
|
|
|
SceneMap::iterator it = fScenes.begin();
|
|
|
|
for (; it != fScenes.end(); it++)
|
|
|
|
{
|
|
|
|
NxScene* scene = it->second;
|
|
|
|
UInt32 numActors = scene->getNbActors();
|
|
|
|
NxActor** actors = scene->getActors();
|
|
|
|
|
|
|
|
for (int i = 0; i < numActors; i++)
|
|
|
|
{
|
|
|
|
plPXPhysical* physical = (plPXPhysical*)actors[i]->userData;
|
|
|
|
if (physical)
|
|
|
|
{
|
|
|
|
// apply any hit forces
|
|
|
|
physical->ApplyHitForce();
|
|
|
|
|
|
|
|
if (physical->GetSceneNode())
|
|
|
|
{
|
|
|
|
physical->SendNewLocation();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if there's no scene node, it's not active (probably about to be collected)
|
|
|
|
const plKey physKey = physical->GetKey();
|
|
|
|
if (physKey)
|
|
|
|
{
|
|
|
|
const char *physName = physical->GetKeyName();
|
|
|
|
if (physName)
|
|
|
|
{
|
|
|
|
plSimulationMgr::Log("Removing physical <%s> because of missing scene node.\n", physName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Remove(physical);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// // iterate through the db types, which are powers-of-two enums.
|
|
|
|
// for( plLOSDB db = static_cast<plLOSDB>(1) ;
|
|
|
|
// db < plSimDefs::kLOSDBMax;
|
|
|
|
// db = static_cast<plLOSDB>(db << 1) )
|
|
|
|
// {
|
|
|
|
// fLOSSolvers[db]->Resolve(fSubspace);
|
|
|
|
// }
|
|
|
|
// if(fNeedLOSCullPhase)
|
|
|
|
// {
|
|
|
|
// for( plLOSDB db = static_cast<plLOSDB>(1) ;
|
|
|
|
// db < plSimDefs::kLOSDBMax;
|
|
|
|
// db = static_cast<plLOSDB>(db << 1) )
|
|
|
|
// {
|
|
|
|
// fLOSSolvers[db]->Resolve(fSubspace);
|
|
|
|
// }
|
|
|
|
// fNeedLOSCullPhase = false;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSimulationMgr::MsgReceive(plMessage *msg)
|
|
|
|
{
|
|
|
|
return hsKeyedObject::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// RESOLUTION & TIMEOUT PARAMETERS
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plSimulationMgr::SetMaxDelta(float maxDelta)
|
|
|
|
{
|
|
|
|
fMaxDelta = maxDelta;
|
|
|
|
}
|
|
|
|
|
|
|
|
float plSimulationMgr::GetMaxDelta() const
|
|
|
|
{
|
|
|
|
return fMaxDelta;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::SetStepsPerSecond(int stepsPerSecond)
|
|
|
|
{
|
|
|
|
fStepSize = 1.0f / (float)stepsPerSecond;
|
|
|
|
}
|
|
|
|
|
|
|
|
int plSimulationMgr::GetStepsPerSecond()
|
|
|
|
{
|
|
|
|
return (int)((1.0 / fStepSize) + 0.5f); // round to nearest int
|
|
|
|
}
|
|
|
|
|
|
|
|
int plSimulationMgr::GetMaterialIdx(NxScene* scene, hsScalar friction, hsScalar restitution)
|
|
|
|
{
|
|
|
|
if (friction == 0.5f && restitution == 0.5f)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Use the nutty PhysX method to search for a matching material
|
|
|
|
#define kNumMatsPerCall 32
|
|
|
|
NxMaterial* materials[kNumMatsPerCall];
|
|
|
|
NxU32 iterator = 0;
|
|
|
|
bool getMore = true;
|
|
|
|
while (getMore)
|
|
|
|
{
|
|
|
|
int numMats = scene->getMaterialArray(materials, kNumMatsPerCall, iterator);
|
|
|
|
|
|
|
|
for (int i = 0; i < numMats; i++)
|
|
|
|
{
|
|
|
|
if (materials[i]->getDynamicFriction() == friction &&
|
|
|
|
materials[i]->getRestitution() == restitution)
|
|
|
|
{
|
|
|
|
return materials[i]->getMaterialIndex();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getMore = (numMats == kNumMatsPerCall);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Couldn't find the material, so create it
|
|
|
|
NxMaterialDesc desc;
|
|
|
|
desc.restitution = restitution;
|
|
|
|
desc.dynamicFriction = friction;
|
|
|
|
desc.staticFriction = friction;
|
|
|
|
NxMaterial* mat = scene->createMaterial(desc);
|
|
|
|
return mat->getMaterialIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// SYNCHRONIZATION
|
|
|
|
// Very much a work in progress.
|
|
|
|
// *** would like to synchronize interacting groups as an atomic unit
|
|
|
|
// *** need a "morphing synch" that incrementally approaches the target
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
const double plSimulationMgr::SynchRequest::kDefaultTime = -1000.0;
|
|
|
|
|
|
|
|
void plSimulationMgr::ConsiderSynch(plPXPhysical* physical, plPXPhysical* other)
|
|
|
|
{
|
|
|
|
if (physical->GetProperty(plSimulationInterface::kNoSynchronize) &&
|
|
|
|
(!other || other->GetProperty(plSimulationInterface::kNoSynchronize)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We only need to sync if a dynamic is colliding with something.
|
|
|
|
// Set it up so the dynamic is in 'physical'
|
|
|
|
if (other && other->GetGroup() == plSimDefs::kGroupDynamic)
|
|
|
|
{
|
|
|
|
plPXPhysical* temp = physical;
|
|
|
|
physical = other;
|
|
|
|
other = temp;
|
|
|
|
}
|
|
|
|
// Neither is dynamic, so we can exit now
|
|
|
|
else if (physical->GetGroup() != plSimDefs::kGroupDynamic)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool syncPhys = !physical->GetProperty(plSimulationInterface::kNoSynchronize) &&
|
|
|
|
physical->IsDynamic() &&
|
|
|
|
physical->IsLocallyOwned();
|
|
|
|
bool syncOther = other &&
|
|
|
|
!other->GetProperty(plSimulationInterface::kNoSynchronize) &&
|
|
|
|
other->IsDynamic() != 0.f &&
|
|
|
|
other->IsLocallyOwned();
|
|
|
|
|
|
|
|
if (syncPhys)
|
|
|
|
{
|
|
|
|
double timeNow = hsTimer::GetSysSeconds();
|
|
|
|
double timeElapsed = timeNow - physical->GetLastSyncTime();
|
|
|
|
|
|
|
|
// If both objects are capable of syncing, we want to do it at the same
|
|
|
|
// time, so no interpenetration issues pop up on other clients
|
|
|
|
if (syncOther)
|
|
|
|
timeElapsed = hsMaximum(timeElapsed, timeNow - other->GetLastSyncTime());
|
|
|
|
|
|
|
|
// Set the sync time to 1 second from the last sync
|
|
|
|
double syncTime = 0.0;
|
|
|
|
if (timeElapsed > 1.0)
|
|
|
|
syncTime = hsTimer::GetSysSeconds();
|
|
|
|
else
|
|
|
|
syncTime = hsTimer::GetSysSeconds() + (1.0 - timeElapsed);
|
|
|
|
|
|
|
|
// This line will create and insert the request if it's not there already.
|
|
|
|
SynchRequest& physReq = fPendingSynchs[physical];
|
|
|
|
if (physReq.fTime == SynchRequest::kDefaultTime)
|
|
|
|
physReq.fKey = physical->GetKey();
|
|
|
|
physReq.fTime = syncTime;
|
|
|
|
|
|
|
|
if (syncOther)
|
|
|
|
{
|
|
|
|
SynchRequest& otherReq = fPendingSynchs[other];
|
|
|
|
if (otherReq.fTime == SynchRequest::kDefaultTime)
|
|
|
|
otherReq.fKey = other->GetKey();
|
|
|
|
otherReq.fTime = syncTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::IProcessSynchs()
|
|
|
|
{
|
|
|
|
double time = hsTimer::GetSysSeconds();
|
|
|
|
|
|
|
|
PhysSynchMap::iterator i = fPendingSynchs.begin();
|
|
|
|
|
|
|
|
while (i != fPendingSynchs.end())
|
|
|
|
{
|
|
|
|
SynchRequest req = (*i).second;
|
|
|
|
if (req.fKey->ObjectIsLoaded())
|
|
|
|
{
|
|
|
|
plPXPhysical* phys = (*i).first;
|
|
|
|
bool timesUp = (time >= req.fTime);
|
|
|
|
bool allQuiet = false;//phys->GetActo GetBody()->isActive() == false;
|
|
|
|
|
|
|
|
if (timesUp || allQuiet)
|
|
|
|
{
|
|
|
|
phys->DirtySynchState(kSDLPhysical, plSynchedObject::kBCastToClients);
|
|
|
|
i = fPendingSynchs.erase(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i = fPendingSynchs.erase(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::Log(const char * fmt, ...)
|
|
|
|
{
|
|
|
|
if(gTheInstance)
|
|
|
|
{
|
|
|
|
plStatusLog* log = GetInstance()->fLog;
|
|
|
|
if(log)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
log->AddLineV(fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::LogV(const char* formatStr, va_list args)
|
|
|
|
{
|
|
|
|
if(gTheInstance)
|
|
|
|
{
|
|
|
|
plStatusLog * log = GetInstance()->fLog;
|
|
|
|
if(log)
|
|
|
|
{
|
|
|
|
log->AddLineV(formatStr, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSimulationMgr::ClearLog()
|
|
|
|
{
|
|
|
|
if(gTheInstance)
|
|
|
|
{
|
|
|
|
plStatusLog *log = GetInstance()->fLog;
|
|
|
|
if(log)
|
|
|
|
{
|
|
|
|
log->Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE
|
|
|
|
#include "../plPipeline/plDebugText.h"
|
|
|
|
|
|
|
|
void plSimulationMgr::IDrawActiveActorList()
|
|
|
|
{
|
|
|
|
plDebugText &debugTxt = plDebugText::Instance();
|
|
|
|
char strBuf[ 2048 ];
|
|
|
|
int lineHeight = debugTxt.GetFontSize() + 4;
|
|
|
|
UInt32 scrnWidth, scrnHeight;
|
|
|
|
|
|
|
|
debugTxt.GetScreenSize( &scrnWidth, &scrnHeight );
|
|
|
|
int y = 10;
|
|
|
|
int x = 10;
|
|
|
|
|
|
|
|
sprintf(strBuf, "Number of scenes: %d", fScenes.size());
|
|
|
|
debugTxt.DrawString(x, y, strBuf);
|
|
|
|
y += lineHeight;
|
|
|
|
int sceneNumber=1;
|
|
|
|
for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++)
|
|
|
|
{
|
|
|
|
|
|
|
|
sprintf(strBuf, "Scene: %s",it->first->GetName());
|
|
|
|
debugTxt.DrawString(x, y, strBuf);
|
|
|
|
y += lineHeight;
|
|
|
|
UInt32 numActors =it->second->getNbActors();
|
|
|
|
NxActor** actors =it->second->getActors();
|
|
|
|
for(UInt32 i=0;i<numActors;i++)
|
|
|
|
{
|
|
|
|
if(!actors[i]->isSleeping())
|
|
|
|
{
|
|
|
|
sprintf(strBuf,"\t%s",actors[i]->getName());
|
|
|
|
debugTxt.DrawString(x, y, strBuf);
|
|
|
|
y += lineHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sceneNumber++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif //PLASMA_EXTERNAL_RELEASE
|