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.
889 lines
28 KiB
889 lines
28 KiB
/*==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 "plPXPhysicalControllerCore.h" |
|
#include "plSimulationMgr.h" |
|
#include "plPXPhysical.h" |
|
#include "plPXConvert.h" |
|
#include "pnSceneObject/plSimulationInterface.h" |
|
#include "pnSceneObject/plSceneObject.h" |
|
#include "plAvatar/plArmatureMod.h" |
|
#include "pnSceneObject/plCoordinateInterface.h" |
|
#include "plDrawable/plDrawableGenerator.h" |
|
#include "plPhysical/plPhysicalProxy.h" |
|
#include "pnMessage/plSetNetGroupIDMsg.h" |
|
#include "plMessage/plCollideMsg.h" |
|
#include "plModifier/plDetectorLog.h" |
|
|
|
#include "plSurface/hsGMaterial.h" // For our proxy |
|
#include "plSurface/plLayerInterface.h" // For our proxy |
|
|
|
#include "NxPhysics.h" |
|
#include "NxCapsuleController.h" |
|
#include "NxCapsuleShape.h" |
|
#include "ControllerManager.h" |
|
|
|
static ControllerManager gControllerMgr; |
|
static std::vector<plPXPhysicalControllerCore*> gControllers; |
|
static bool gRebuildCache = false; |
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE |
|
bool plPXPhysicalControllerCore::fDebugDisplay = false; |
|
#endif // PLASMA_EXTERNAL_RELEASE |
|
int plPXPhysicalControllerCore::fPXControllersMax = 0; |
|
|
|
#define kCCTSkinWidth 0.1f |
|
#define kCCTStepOffset 0.7f |
|
#define kCCTZOffset ((fRadius + (fHeight / 2)) + kCCTSkinWidth) |
|
#define kPhysHeightCorrection 0.8f |
|
#define kPhysZOffset ((kCCTZOffset + (kPhysHeightCorrection / 2)) - 0.05f) |
|
#define kAvatarMass 200.0f |
|
|
|
static class PXControllerHitReport : public NxUserControllerHitReport |
|
{ |
|
public: |
|
virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) |
|
{ |
|
plPXPhysicalControllerCore* controller = (plPXPhysicalControllerCore*)hit.controller->getUserData(); |
|
NxActor& actor = hit.shape->getActor(); |
|
plPXPhysical* phys = (plPXPhysical*)actor.userData; |
|
hsVector3 normal = plPXConvert::Vector(hit.worldNormal); |
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE |
|
plDbgCollisionInfo info; |
|
info.fNormal = normal; |
|
info.fSO = plSceneObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); |
|
|
|
NxCapsule capsule; |
|
controller->GetWorldSpaceCapsule(capsule); |
|
info.fOverlap = hit.shape->checkOverlapCapsule(capsule); |
|
|
|
controller->fDbgCollisionInfo.Append(info); |
|
#endif PLASMA_EXTERNAL_RELEASE |
|
|
|
// If the avatar hit a movable physical, apply some force to it. |
|
if (actor.isDynamic()) |
|
{ |
|
if (!actor.readBodyFlag(NX_BF_KINEMATIC) && !actor.readBodyFlag(NX_BF_FROZEN)) |
|
{ |
|
// Don't apply force when standing on top of an object. |
|
if (normal.fZ < 0.85f) |
|
{ |
|
hsVector3 velocity = controller->GetLinearVelocity(); |
|
velocity.fZ = 0.0f; |
|
float length = velocity.Magnitude(); |
|
if (length > 0) |
|
{ |
|
// Only allow horizontal pushes for now |
|
NxVec3 hitDir = hit.worldPos - hit.controller->getPosition(); |
|
hitDir.z = 0.0f; |
|
hitDir.normalize(); |
|
|
|
// Get controller speed along the hitDir |
|
float cctProj = velocity.fX * hitDir.x + velocity.fY * hitDir.y; |
|
length = length + cctProj / 2.0f; |
|
|
|
// Get hit actors speed along the hitDir |
|
float hitProj = actor.getLinearVelocity().dot(hitDir); |
|
if (hitProj > 0) |
|
length -= hitProj; |
|
|
|
length *= kAvatarMass; |
|
|
|
hsPoint3 pos((float)hit.worldPos.x, (float)hit.worldPos.y, (float)hit.worldPos.z); |
|
phys->SetHitForce(plPXConvert::Vector(hitDir * length), pos); |
|
controller->AddDynamicHit(phys); |
|
} |
|
} |
|
} |
|
} |
|
else // else if the avatar hit a static |
|
{ |
|
controller->fMovementStrategy->AddContactNormals(normal); |
|
return NX_ACTION_NONE; |
|
} |
|
if (phys && phys->GetProperty(plSimulationInterface::kAvAnimPushable)) |
|
{ |
|
hsQuat inverseRotation = controller->fLocalRotation.Inverse(); |
|
hsVector3 normal = plPXConvert::Vector(hit.worldNormal); |
|
controller->SetPushingPhysical(phys); |
|
controller->SetFacingPushingPhysical((inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false)); |
|
} |
|
return NX_ACTION_NONE; |
|
} |
|
virtual NxControllerAction onControllerHit(const NxControllersHit& hit) |
|
{ |
|
return NX_ACTION_NONE; |
|
} |
|
} gControllerHitReport; |
|
|
|
plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, float height, float width, bool human) |
|
{ |
|
if (!plPXPhysicalControllerCore::fPXControllersMax || gControllers.size() < plPXPhysicalControllerCore::fPXControllersMax) |
|
{ |
|
float radius = width / 2.0f; |
|
float realHeight = height - width; |
|
return new plPXPhysicalControllerCore(ownerSO, realHeight, radius, human); |
|
} |
|
return nil; |
|
} |
|
|
|
plPXPhysicalControllerCore::plPXPhysicalControllerCore(plKey ownerSO, float height, float radius, bool human) |
|
: plPhysicalControllerCore(ownerSO, height, radius), |
|
fController(nil), |
|
fActor(nil), |
|
fProxyGen(nil), |
|
fKinematicCCT(true), |
|
fHuman(human) |
|
{ |
|
ICreateController(fLocalPosition); |
|
fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); |
|
gControllers.push_back(this); |
|
} |
|
plPXPhysicalControllerCore::~plPXPhysicalControllerCore() |
|
{ |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
if (gControllers[i] == this) |
|
{ |
|
gControllers.erase(gControllers.begin()+i); |
|
break; |
|
} |
|
} |
|
IDeleteController(); |
|
|
|
// Release any queued messages we may have |
|
int numMsgs = fQueuedCollideMsgs.size(); |
|
if (numMsgs) |
|
{ |
|
for (int i = 0; i < numMsgs; ++i) |
|
delete fQueuedCollideMsgs[i]; |
|
|
|
fQueuedCollideMsgs.clear(); |
|
} |
|
|
|
delete fProxyGen; |
|
} |
|
|
|
void plPXPhysicalControllerCore::Enable(bool enable) |
|
{ |
|
if (fEnabled != enable) |
|
{ |
|
fEnabled = enable; |
|
if (fEnabled) |
|
{ |
|
// Defer until the next physics update. |
|
fEnableChanged = true; |
|
} |
|
else |
|
{ |
|
if (!fKinematicCCT) |
|
{ |
|
// Dynamic controllers are forced kinematic |
|
fActor->raiseBodyFlag(NX_BF_KINEMATIC); |
|
NxShape* shape = fActor->getShapes()[0]; |
|
shape->setGroup(plSimDefs::kGroupAvatarKinematic); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void plPXPhysicalControllerCore::SetSubworld(plKey world) |
|
{ |
|
if (fWorldKey != world) |
|
{ |
|
SimLog("Changing subworlds!"); |
|
|
|
// Inform detectors in the old world that we are leaving |
|
IInformDetectors(false); |
|
IDeleteController(); |
|
|
|
// We need our real global location here, not the interpolated location |
|
fLocalRotation.MakeMatrix(&fLastGlobalLoc); |
|
fLastGlobalLoc.SetTranslate(&fLocalPosition); |
|
if (fWorldKey) |
|
{ |
|
hsMatrix44 prevSubL2W; |
|
fPrevSubworldW2L.GetInverse(&prevSubL2W); |
|
fLastGlobalLoc = prevSubL2W * fLastGlobalLoc; |
|
} |
|
// Update our scene object so the change isn't wiped out |
|
plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); |
|
if (so) |
|
{ |
|
hsMatrix44 globalLocInv; |
|
fLastGlobalLoc.GetInverse(&globalLocInv); |
|
so->SetTransform(fLastGlobalLoc, globalLocInv); |
|
so->FlushTransform(); |
|
} |
|
|
|
// Update Local Position and rotation |
|
fWorldKey = world; |
|
const plCoordinateInterface* subworldCI = GetSubworldCI(); |
|
if (subworldCI) |
|
{ |
|
fPrevSubworldW2L = subworldCI->GetWorldToLocal(); |
|
hsMatrix44 l2s = fPrevSubworldW2L * fLastGlobalLoc; |
|
l2s.GetTranslate(&fLocalPosition); |
|
fLocalRotation.SetFromMatrix44(l2s); |
|
} |
|
else |
|
{ |
|
fPrevSubworldW2L.Reset(); |
|
fLastGlobalLoc.GetTranslate(&fLocalPosition); |
|
fLocalRotation.SetFromMatrix44(fLastGlobalLoc); |
|
} |
|
|
|
fLastLocalPosition = fLocalPosition; |
|
|
|
// Create new controller |
|
ICreateController(fLocalPosition); |
|
RebuildCache(); |
|
} |
|
} |
|
|
|
void plPXPhysicalControllerCore::GetState(hsPoint3& pos, float& zRot) |
|
{ |
|
// Temporarily use the position point while we get the z rotation |
|
fLocalRotation.NormalizeIfNeeded(); |
|
fLocalRotation.GetAngleAxis(&zRot, (hsVector3*)&pos); |
|
|
|
if (pos.fZ < 0) |
|
zRot = (2 * float(M_PI)) - zRot; // axis is backwards, so reverse the angle too |
|
|
|
pos = fLocalPosition; |
|
} |
|
void plPXPhysicalControllerCore::SetState(const hsPoint3& pos, float zRot) |
|
{ |
|
plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); |
|
if (so) |
|
{ |
|
hsQuat worldRot; |
|
hsVector3 zAxis(0.f, 0.f, 1.f); |
|
worldRot.SetAngleAxis(zRot, zAxis); |
|
|
|
hsMatrix44 l2w, w2l; |
|
worldRot.MakeMatrix(&l2w); |
|
l2w.SetTranslate(&pos); |
|
|
|
// Localize new position and rotation to global coords if we're in a subworld |
|
const plCoordinateInterface* ci = GetSubworldCI(); |
|
if (ci) |
|
{ |
|
const hsMatrix44& subworldL2W = ci->GetLocalToWorld(); |
|
l2w = subworldL2W * l2w; |
|
} |
|
l2w.GetInverse(&w2l); |
|
so->SetTransform(l2w, w2l); |
|
so->FlushTransform(); |
|
} |
|
} |
|
|
|
void plPXPhysicalControllerCore::SetMovementStrategy(plMovementStrategy* strategy) |
|
{ |
|
if (fKinematicCCT != strategy->IsKinematic()) |
|
{ |
|
IDeleteController(); |
|
fKinematicCCT = !fKinematicCCT; |
|
ICreateController(fLocalPosition); |
|
} |
|
|
|
fMovementStrategy = strategy; |
|
} |
|
|
|
void plPXPhysicalControllerCore::SetGlobalLoc(const hsMatrix44& l2w) |
|
{ |
|
fLastGlobalLoc = l2w; |
|
|
|
// Update our local position and rotation |
|
hsPoint3 prevPosition = fLocalPosition; |
|
const plCoordinateInterface* subworldCI = GetSubworldCI(); |
|
if (subworldCI) |
|
{ |
|
hsMatrix44 l2s = fPrevSubworldW2L * l2w; |
|
|
|
l2s.GetTranslate(&fLocalPosition); |
|
fLocalRotation.SetFromMatrix44(l2s); |
|
} |
|
else |
|
{ |
|
l2w.GetTranslate(&fLocalPosition); |
|
fLocalRotation.SetFromMatrix44(l2w); |
|
} |
|
|
|
fLastLocalPosition = fLocalPosition; |
|
|
|
if (fProxyGen) |
|
{ |
|
hsMatrix44 w2l; |
|
l2w.GetInverse(&w2l); |
|
fProxyGen->SetTransform(l2w, w2l); |
|
} |
|
|
|
// Update the physical position |
|
if (fKinematicCCT) |
|
{ |
|
hsVector3 disp(&fLocalPosition, &prevPosition); |
|
if (disp.Magnitude() > 2.f) |
|
{ |
|
// Teleport the underlying actor most of the way |
|
disp.Normalize(); |
|
disp *= 0.001f; |
|
|
|
hsPoint3 teleportPos = fLocalPosition - disp; |
|
NxVec3 pos(teleportPos.fX, teleportPos.fY, teleportPos.fZ + kPhysZOffset); |
|
fActor->setGlobalPosition(pos); |
|
} |
|
|
|
NxExtendedVec3 extPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kCCTZOffset); |
|
fController->setPosition(extPos); |
|
} |
|
else |
|
{ |
|
NxVec3 pos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kPhysZOffset); |
|
if (fActor->readBodyFlag(NX_BF_KINEMATIC)) |
|
fActor->moveGlobalPosition(pos); |
|
else |
|
fActor->setGlobalPosition(pos); |
|
} |
|
} |
|
|
|
void plPXPhysicalControllerCore::GetPositionSim(hsPoint3& pos) |
|
{ |
|
if (fKinematicCCT) |
|
{ |
|
const NxExtendedVec3& extPos = fController->getPosition(); |
|
pos.Set((float)extPos.x, (float)extPos.y, (float)extPos.z - kCCTZOffset); |
|
} |
|
else |
|
{ |
|
NxVec3 nxPos = fActor->getGlobalPosition(); |
|
pos.Set(nxPos.x, nxPos.y, nxPos.z - kPhysZOffset); |
|
} |
|
} |
|
|
|
void plPXPhysicalControllerCore::Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) |
|
{ |
|
NxU32 colFlags = 0; |
|
|
|
fController->move(plPXConvert::Vector(displacement), collideWith, 0.00001f, colFlags); |
|
|
|
collisionResults = 0; |
|
if (colFlags & NXCC_COLLISION_DOWN) |
|
collisionResults |= kBottom; |
|
if (colFlags & NXCC_COLLISION_UP) |
|
collisionResults |= kTop; |
|
if (colFlags & NXCC_COLLISION_SIDES) |
|
collisionResults |= kSides; |
|
} |
|
|
|
void plPXPhysicalControllerCore::SetLinearVelocitySim(const hsVector3& linearVel) |
|
{ |
|
if (!fKinematicCCT) |
|
{ |
|
NxVec3 vel = plPXConvert::Vector(linearVel); |
|
fActor->setLinearVelocity(vel); |
|
} |
|
} |
|
|
|
int plPXPhysicalControllerCore::SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, bool vsStatics, |
|
uint32_t& vsSimGroups, std::vector<plControllerSweepRecord>& hits) |
|
{ |
|
NxSweepQueryHit queryHit[10]; |
|
|
|
unsigned int flags = NX_SF_ALL_HITS; |
|
if (vsDynamics) |
|
flags |= NX_SF_DYNAMICS; |
|
if (vsStatics) |
|
flags |= NX_SF_STATICS; |
|
|
|
NxVec3 vec; |
|
vec.x = endPos.fX - startPos.fX; |
|
vec.y = endPos.fY - startPos.fY; |
|
vec.z = endPos.fZ - startPos.fZ; |
|
|
|
NxShape* shape = fActor->getShapes()[0]; |
|
NxCapsuleShape* capShape = shape->isCapsule(); |
|
float radius = capShape->getRadius(); |
|
float height = capShape->getHeight(); |
|
|
|
NxCapsule capsule; |
|
capsule.p0 = plPXConvert::Point(startPos); |
|
capsule.p0.z = capsule.p0.z + radius; |
|
capsule.radius = radius; |
|
capsule.p1 = capsule.p0; |
|
capsule.p1.z = capsule.p1.z + height; |
|
|
|
NxScene *scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); |
|
int numHits = scene->linearCapsuleSweep(capsule, vec, flags, nil, 10, queryHit, nil, vsSimGroups); |
|
if (numHits) |
|
{ |
|
for (int i = 0; i < numHits; ++i) |
|
{ |
|
plControllerSweepRecord currentHit; |
|
currentHit.ObjHit = (plPhysical*)queryHit[i].hitShape->getActor().userData; |
|
if (currentHit.ObjHit) |
|
{ |
|
currentHit.Point = plPXConvert::Point(queryHit[i].point); |
|
currentHit.Normal = plPXConvert::Vector(queryHit[i].normal); |
|
hits.push_back(currentHit); |
|
} |
|
} |
|
} |
|
|
|
return hits.size(); |
|
} |
|
|
|
void plPXPhysicalControllerCore::LeaveAge() |
|
{ |
|
SetPushingPhysical(nil); |
|
if (fWorldKey) |
|
SetSubworld(nil); |
|
|
|
// Disable all collisions |
|
fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); |
|
} |
|
|
|
void plPXPhysicalControllerCore::GetWorldSpaceCapsule(NxCapsule& cap) const |
|
{ |
|
NxShape* shape = fActor->getShapes()[0]; |
|
NxCapsuleShape* capShape = shape->isCapsule(); |
|
capShape->getWorldCapsule(cap); |
|
} |
|
|
|
plDrawableSpans* plPXPhysicalControllerCore::CreateProxy(hsGMaterial* mat, hsTArray<uint32_t>& idx, plDrawableSpans* addTo) |
|
{ |
|
// FIXME |
|
plDrawableSpans* myDraw = addTo; |
|
bool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); |
|
float radius = fRadius; |
|
myDraw = plDrawableGenerator::GenerateSphericalDrawable(fLocalPosition, radius, |
|
mat, fLastGlobalLoc, blended, |
|
nil, &idx, myDraw); |
|
|
|
/* |
|
plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); |
|
if (so) |
|
{ |
|
bool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); |
|
|
|
myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, |
|
mat, so->GetLocalToWorld(), blended, |
|
nil, &idx, myDraw); |
|
} |
|
*/ |
|
return myDraw; |
|
} |
|
|
|
void plPXPhysicalControllerCore::AddDynamicHit(plPXPhysical* phys) |
|
{ |
|
int numHits = fDynamicHits.size(); |
|
for (int i = 0; i < numHits; ++i) |
|
{ |
|
if (fDynamicHits[i] == phys) |
|
return; |
|
} |
|
|
|
fDynamicHits.push_back(phys); |
|
} |
|
|
|
void plPXPhysicalControllerCore::Apply(float delSecs) |
|
{ |
|
plPXPhysicalControllerCore* controller; |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
controller = gControllers[i]; |
|
if (gRebuildCache && controller->fController) |
|
controller->fController->reportSceneChanged(); |
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE |
|
controller->fDbgCollisionInfo.SetCount(0); |
|
#endif |
|
|
|
controller->IDispatchQueuedMsgs(); |
|
controller->IApply(delSecs); |
|
controller->IProcessDynamicHits(); |
|
} |
|
|
|
gRebuildCache = false; |
|
} |
|
void plPXPhysicalControllerCore::Update(int numSubSteps, float alpha) |
|
{ |
|
gControllerMgr.updateControllers(); |
|
|
|
plPXPhysicalControllerCore* controller; |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
controller = gControllers[i]; |
|
|
|
controller->IUpdate(numSubSteps, alpha); |
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE |
|
if (fDebugDisplay) |
|
controller->IDrawDebugDisplay(i); |
|
#endif |
|
} |
|
} |
|
void plPXPhysicalControllerCore::UpdateNonPhysical(float alpha) |
|
{ |
|
plPXPhysicalControllerCore* controller; |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
controller = gControllers[i]; |
|
controller->IUpdateNonPhysical(alpha); |
|
} |
|
} |
|
|
|
void plPXPhysicalControllerCore::RebuildCache() { gRebuildCache = true; } |
|
|
|
plPXPhysicalControllerCore* plPXPhysicalControllerCore::GetController(NxActor& actor) |
|
{ |
|
plPXPhysicalControllerCore* controller; |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
controller = gControllers[i]; |
|
if (controller->fActor == &actor) |
|
return controller; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
bool plPXPhysicalControllerCore::AnyControllersInThisWorld(plKey world) |
|
{ |
|
plPXPhysicalControllerCore* controller; |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
controller = gControllers[i]; |
|
if (controller->GetSubworld() == world) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
int plPXPhysicalControllerCore::GetNumberOfControllersInThisSubWorld(plKey world) |
|
{ |
|
int count = 0; |
|
plPXPhysicalControllerCore* controller; |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
controller = gControllers[i]; |
|
if (controller->GetSubworld() == world) |
|
++count; |
|
} |
|
|
|
return count; |
|
} |
|
int plPXPhysicalControllerCore::GetControllersInThisSubWorld(plKey world, int maxToReturn, plPXPhysicalControllerCore** bufferout) |
|
{ |
|
int count = 0; |
|
plPXPhysicalControllerCore* controller; |
|
int numControllers = gControllers.size(); |
|
for (int i = 0; i < numControllers; ++i) |
|
{ |
|
controller = gControllers[i]; |
|
if (controller->GetSubworld() == world) |
|
{ |
|
if (count < maxToReturn) |
|
{ |
|
bufferout[count] = controller; |
|
++count; |
|
} |
|
} |
|
} |
|
|
|
return count; |
|
} |
|
|
|
int plPXPhysicalControllerCore::NumControllers() { return gControllers.size(); } |
|
|
|
void plPXPhysicalControllerCore::IHandleEnableChanged() |
|
{ |
|
// Defered enable |
|
fEnableChanged = false; |
|
|
|
if (!fKinematicCCT) |
|
{ |
|
// Restore dynamic controller |
|
fActor->clearBodyFlag(NX_BF_KINEMATIC); |
|
NxShape* shape = fActor->getShapes()[0]; |
|
shape->setGroup(plSimDefs::kGroupAvatar); |
|
} |
|
|
|
// Enable actor collisions |
|
if (fActor->readActorFlag(NX_AF_DISABLE_COLLISION)) |
|
fActor->clearActorFlag(NX_AF_DISABLE_COLLISION); |
|
} |
|
|
|
void plPXPhysicalControllerCore::IInformDetectors(bool entering) |
|
{ |
|
static const NxU32 kDetectorFlag = 1<<plSimDefs::kGroupDetector; |
|
static const int kNumShapes = 30; |
|
|
|
DetectorLog("Informing from plPXPhysicalControllerCore::IInformDetectors"); |
|
|
|
NxShape* shapes[kNumShapes]; |
|
NxCapsule capsule; |
|
GetWorldSpaceCapsule(capsule); |
|
NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); |
|
int numCollided = scene->overlapCapsuleShapes(capsule, NX_ALL_SHAPES, kNumShapes, shapes, NULL, kDetectorFlag, NULL, true); |
|
for (int i = 0; i < numCollided; ++i) |
|
{ |
|
plPXPhysical* physical = (plPXPhysical*)shapes[i]->getActor().userData; |
|
if (physical && physical->DoReportOn(plSimDefs::kGroupAvatar)) |
|
{ |
|
plCollideMsg* msg = new plCollideMsg(); |
|
msg->fOtherKey = fOwner; |
|
msg->fEntering = entering; |
|
msg->AddReceiver(physical->GetObjectKey()); |
|
|
|
// Queue until the next sim step |
|
fQueuedCollideMsgs.push_back(msg); |
|
} |
|
} |
|
|
|
DetectorLog("Done informing from plPXPhysicalControllerCore::IInformDetectors"); |
|
} |
|
|
|
void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos) |
|
{ |
|
NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); |
|
|
|
if (fKinematicCCT) |
|
{ |
|
// Use PhysX character controller |
|
NxCapsuleControllerDesc desc; |
|
desc.position.x = pos.fX; |
|
desc.position.y = pos.fY; |
|
desc.position.z = pos.fZ + kCCTZOffset; |
|
desc.upDirection = NX_Z; |
|
desc.slopeLimit = kSlopeLimit; |
|
desc.skinWidth = kCCTSkinWidth; |
|
desc.stepOffset = kCCTStepOffset; |
|
desc.callback = &gControllerHitReport; |
|
desc.userData = this; |
|
desc.radius = fRadius; |
|
desc.height = fHeight; |
|
desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; |
|
fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); |
|
fActor = fController->getActor(); |
|
|
|
// Set the actor group - Dynamics are in group 1 and will report on everything in group 0. |
|
// We don't want notifications |
|
fActor->setGroup(2); |
|
|
|
// Set the shape group. Not used by the NxController itself, |
|
// But required for correct group interactions in the simulation. |
|
NxShape* shape = fActor->getShapes()[0]; |
|
shape->setGroup(plSimDefs::kGroupAvatarKinematic); |
|
|
|
// In PhysX 2, the kinematic actors scale factor isn't exposed. |
|
// It is hardcoded at 0.8 which doesn't suit, so we have to manually adjust its dimensions. |
|
float kineRadius = fRadius + kCCTSkinWidth; |
|
float kineHeight = fHeight; |
|
NxCapsuleShape* capShape = shape->isCapsule(); |
|
if (fHuman) |
|
{ |
|
kineHeight += kPhysHeightCorrection; |
|
capShape->setLocalPosition(NxVec3(0.0f, (kPhysHeightCorrection / 2.0f), 0.0f)); |
|
} |
|
capShape->setDimensions(kineRadius, kineHeight); |
|
} |
|
else |
|
{ |
|
// Use dynamic actor for the character controller |
|
NxCapsuleShapeDesc capDesc; |
|
capDesc.materialIndex = plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0f, 0.0f); |
|
capDesc.radius = fRadius + kCCTSkinWidth; |
|
capDesc.height = fHeight + kPhysHeightCorrection; |
|
|
|
NxBodyDesc bodyDesc; |
|
bodyDesc.mass = kAvatarMass; |
|
bodyDesc.flags = NX_BF_DISABLE_GRAVITY; |
|
bodyDesc.flags |= NX_BF_FROZEN_ROT; |
|
|
|
if (fEnabled) |
|
capDesc.group = plSimDefs::kGroupAvatar; |
|
else |
|
{ |
|
bodyDesc.flags |= NX_BF_KINEMATIC; |
|
capDesc.group = plSimDefs::kGroupAvatarKinematic; |
|
} |
|
|
|
NxActorDesc actorDesc; |
|
actorDesc.shapes.pushBack(&capDesc); |
|
actorDesc.body = &bodyDesc; |
|
actorDesc.group = 2; |
|
|
|
actorDesc.globalPose.M.rotX(NxHalfPiF32); |
|
actorDesc.globalPose.t.x = pos.fX; |
|
actorDesc.globalPose.t.y = pos.fY; |
|
actorDesc.globalPose.t.z = pos.fZ + kPhysZOffset; |
|
|
|
fActor = scene->createActor(actorDesc); |
|
} |
|
|
|
fSeeking = false; |
|
|
|
// Create proxy for the debug display |
|
/* FIXME |
|
// the avatar proxy doesn't seem to work... not sure why? |
|
hsColorRGBA physColor; |
|
float opac = 1.0f; |
|
|
|
// local avatar is light purple and transparent |
|
physColor.Set(.2f, .1f, .2f, 1.f); |
|
opac = 0.8f; |
|
|
|
fProxyGen = new plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); |
|
fProxyGen->Init(this); |
|
*/ |
|
} |
|
void plPXPhysicalControllerCore::IDeleteController() |
|
{ |
|
if (fKinematicCCT) |
|
{ |
|
gControllerMgr.releaseController(*fController); |
|
fController = nil; |
|
} |
|
else |
|
{ |
|
NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); |
|
scene->releaseActor(*fActor); |
|
} |
|
|
|
fActor = nil; |
|
plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); |
|
} |
|
|
|
void plPXPhysicalControllerCore::IDispatchQueuedMsgs() |
|
{ |
|
int numMsgs = fQueuedCollideMsgs.size(); |
|
if (numMsgs) |
|
{ |
|
plSimulationMgr* simMgr = plSimulationMgr::GetInstance(); |
|
for (int i = 0; i < numMsgs; ++i) |
|
simMgr->AddCollisionMsg(fQueuedCollideMsgs[i]); |
|
|
|
fQueuedCollideMsgs.clear(); |
|
} |
|
} |
|
void plPXPhysicalControllerCore::IProcessDynamicHits() |
|
{ |
|
int numHits = fDynamicHits.size(); |
|
if (numHits) |
|
{ |
|
plPXPhysical* phys; |
|
plSimulationMgr* simMgr = plSimulationMgr::GetInstance(); |
|
for (int i = 0; i < numHits; ++i) |
|
{ |
|
phys = fDynamicHits[i]; |
|
|
|
// If this is the local avatar, we need to take ownership of this dynamic if we haven't already |
|
if (fLOSDB == plSimDefs::kLOSDBLocalAvatar && !phys->IsLocallyOwned() && !phys->GetProperty(plSimulationInterface::kNoOwnershipChange)) |
|
{ |
|
plSynchedObject* obj = plSynchedObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); |
|
obj->SetNetGroupConstant(plNetGroup::kNetGroupLocalPhysicals); |
|
|
|
plSetNetGroupIDMsg* setNetGroupID = new plSetNetGroupIDMsg; |
|
setNetGroupID->fId = plNetGroup::kNetGroupRemotePhysicals; |
|
setNetGroupID->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); |
|
setNetGroupID->SetBCastFlag(plMessage::kLocalPropagate, false); |
|
setNetGroupID->Send(obj->GetKey()); |
|
} |
|
|
|
phys->ApplyHitForce(); |
|
simMgr->ConsiderSynch(phys, nil); |
|
} |
|
fDynamicHits.clear(); |
|
} |
|
} |
|
|
|
#ifndef PLASMA_EXTERNAL_RELEASE |
|
#include "../plPipeline/plDebugText.h" |
|
|
|
void plPXPhysicalControllerCore::IDrawDebugDisplay(int controllerIdx) |
|
{ |
|
plDebugText &debugTxt = plDebugText::Instance(); |
|
plString debugString; |
|
int lineHeight = debugTxt.GetFontSize() + 4; |
|
int x = 10; // Initial draw position |
|
static int y = 10; |
|
if (controllerIdx == 0) |
|
{ |
|
y = 10; |
|
debugString = plFormat("Controller Count: {}", gControllers.size()); |
|
debugTxt.DrawString(x, y, debugString.c_str()); |
|
y += lineHeight; |
|
} |
|
|
|
// Only display avatar collisions if any exist... |
|
int collisionCount = fDbgCollisionInfo.GetCount(); |
|
if (collisionCount > 0) |
|
{ |
|
debugString = plFormat("Controller #{} ({}) Collisions:", |
|
controllerIdx + 1, gControllers[controllerIdx]->fOwner->GetName()); |
|
debugTxt.DrawString(x, y, debugString.c_str()); |
|
y += lineHeight; |
|
|
|
for (int i = 0; i < collisionCount; i++) |
|
{ |
|
hsVector3 normal = fDbgCollisionInfo[i].fNormal; |
|
const char* overlapStr = fDbgCollisionInfo[i].fOverlap ? "yes" : "no"; |
|
float angle = hsRadiansToDegrees(acos(normal * hsVector3(0, 0, 1))); |
|
debugString = plString::Format("\tObj: %s, Normal: (%.2f, %.2f, %.2f), Angle(%.1f), Overlap(%s)", |
|
fDbgCollisionInfo[i].fSO->GetKeyName().c_str(), |
|
normal.fX, normal.fY, normal.fZ, angle, |
|
overlapStr); |
|
debugTxt.DrawString(x, y, debugString.c_str()); |
|
y += lineHeight; |
|
} |
|
} |
|
} |
|
#endif PLASMA_EXTERNAL_RELEASE |
|
|
|
|