From 70e766ae5b346a8d1019ca5bfae0053206b1ecd5 Mon Sep 17 00:00:00 2001 From: Skoader Date: Sun, 24 Jun 2012 13:13:21 +1000 Subject: [PATCH] Updated plPXPhysicalControllerCore While based heavily on the old implementation, this is essentially a rewrite. Notable changes - Controllers are now updated at the same fixed frequency as the simulation. Resulting output is interpolated between steps to precisely match the frame delta. Physics work is only done when enough time has passed to perform a step. The kinematic actor that followed around the controller has been removed. The underlying kinematic actor created by the NxController is now used for triggering. A new sim group was added for a kinematically controlled avatar. 2 unused files removed - plPXPhysicalController.h & plPXPhysicalController.cpp --- .../PubUtilLib/plPhysX/plLOSDispatch.cpp | 3 +- .../plPhysX/plPXPhysicalController.cpp | 1280 --------------- .../plPhysX/plPXPhysicalController.h | 211 --- .../plPhysX/plPXPhysicalControllerCore.cpp | 1416 +++++++---------- .../plPhysX/plPXPhysicalControllerCore.h | 160 +- .../PubUtilLib/plPhysX/plSimulationMgr.cpp | 118 +- .../PubUtilLib/plPhysX/plSimulationMgr.h | 3 + .../Plasma/PubUtilLib/plPhysical/plSimDefs.h | 4 +- 8 files changed, 669 insertions(+), 2526 deletions(-) delete mode 100644 Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp delete mode 100644 Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp index 43ad60a0..27964d8f 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp @@ -94,8 +94,7 @@ private: } else { - bool isController; - plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(hitActor,&isController); + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(hitActor); if (controller) { objKey = controller->GetOwner(); diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp deleted file mode 100644 index 7e8e4667..00000000 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp +++ /dev/null @@ -1,1280 +0,0 @@ -/*==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 . - -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 "plPXPhysicalController.h" -#include "plSimulationMgr.h" -#include "plPXPhysical.h" -#include "plPXConvert.h" -#include "pnSceneObject/plSimulationInterface.h" -#include "pnSceneObject/plSceneObject.h" -#include "pnMessage/plCorrectionMsg.h" -#include "plAvatar/plArmatureMod.h" -#include "pnSceneObject/plCoordinateInterface.h" -#include "plDrawable/plDrawableGenerator.h" -#include "plPhysical/plPhysicalProxy.h" - -#include "pnMessage/plSetNetGroupIDMsg.h - -#include "plSurface/hsGMaterial.h" -#include "plSurface/plLayerInterface.h"" -#include "plMessage/plCollideMsg.h" - - -#include -#include -#include -#include - -#define kPhysxSkinWidth 0.1f -#define kPhysZOffset ((fRadius + (fHeight / 2)) + kPhysxSkinWidth) -#define kSLOPELIMIT (cosf(NxMath::degToRad(55.f))) -//#define kPhysicalHeightFudge 0.4f // this fudge was used for PhysX 2.4 -#define kPhysicalHeightFudge 0.0f - -//#define STEP_OFFSET 1.0f -#define STEP_OFFSET 0.5f -//#define STEP_OFFSET 0.15f - - -#ifndef PLASMA_EXTERNAL_RELEASE -#include "plPipeline/plDebugText.h" -bool plPXPhysicalController::fDebugDisplay = false; -#endif // PLASMA_EXTERNAL_RELEASE - -static ControllerManager gControllerMgr; -static std::vector gControllers; -static bool gRebuildCache = false; - -// KLUDGE: From plPXPhysical.cpp -bool CompareMatrices(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance); - -plPhysicalController* plPhysicalController::Create(plKey ownerSO, hsScalar height, hsScalar width) -{ - hsScalar radius = width / 2.f; - //hsScalar realHeight = height - width; - hsScalar realHeight = height - radius + kPhysicalHeightFudge; - return new plPXPhysicalController(ownerSO, radius, realHeight); -} - -////////////////////////////////////////////////////////////////////////// - -plPXPhysicalController* plPXPhysicalController::FindController(NxController* controller) -{ - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalController* ac = gControllers[i]; - if (ac->fController == controller) - return ac; - } - return nil; -} - -plPXPhysicalController* plPXPhysicalController::GetController(NxActor& actor, bool* isController) -{ - *isController = false; - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalController* ac = gControllers[i]; - if (ac->fController && ac->fController->getActor() == &actor) - { - *isController = true; - return ac; - } - if ( ac->fKinematicActor == &actor) - { - return ac; - } - } - - return nil; -} -void plPXPhysicalController::GetWorldSpaceCapsule(NxCapsule& cap) -{ - if(fController){ - int numshapes=fController->getActor()->getNbShapes(); - if (numshapes==1) - {//there should only be one shape on a controller - NxShape* const *shapes=fController->getActor()->getShapes(); - //and since it is a capsule controller it better be a capsule; - NxCapsuleShape *capShape = shapes[0]->isCapsule(); - if(capShape) capShape->getWorldCapsule(cap); - - } - } - -} -bool plPXPhysicalController::AnyControllersInThisWorld(plKey world) -{ - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalController* ac = gControllers[i]; - if (ac->GetSubworld() == world) - return true; - } - return false; -} - -int plPXPhysicalController::NumControllers() -{ - return gControllers.size(); -} -int plPXPhysicalController::GetControllersInThisSubWorld(plKey world, int maxToReturn,plPXPhysicalController** bufferout) -{ - int i=0; - for (int j=0;jGetSubworld()==world) - { - if(iGetSubworld()==world)i++; - } - return i; -} -void plPXPhysicalController::Update(bool prestep, hsScalar delSecs) -{ - // Apparently the user data field of the controllers is broken -// uint32_t count = gControllerMgr.getNbControllers(); -// NxController* controllers = (NxController*)gControllerMgr.getControllers(); -// -// for (int i = 0; i < count; i++) -// { -// plPXPhysicalController* ac = (plPXPhysicalController*)controllers[i].getAppData(); - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalController* ac = gControllers[i]; - - hsAssert(ac, "Bad avatar controller"); - if (prestep) - { - if (gRebuildCache) - ac->fController->reportSceneChanged(); - ac->IApply(delSecs); - } - else - { - gControllerMgr.updateControllers(); - ac->ISendUpdates(delSecs); - - if (ac->GetSubworldCI()) - ac->fPrevSubworldW2L = ac->GetSubworldCI()->GetWorldToLocal(); - else - { - if (!ac->fPrevSubworldW2L.IsIdentity()) - ac->fPrevSubworldW2L.Reset(); - } - } - } - - gRebuildCache = false; -} - -void plPXPhysicalController::RebuildCache() -{ - gRebuildCache = true; -} -void plPXPhysicalController::IInformDetectors(bool entering) -{ - static const NxU32 DetectorFlag= 1<GetScene(fWorldKey); - int kNumofShapesToStore=30; - NxCapsule cap; - GetWorldSpaceCapsule(cap); - NxShape* shapes[30]; - int numCollided=scene->overlapCapsuleShapes(cap,NX_ALL_SHAPES,kNumofShapesToStore,shapes,NULL,DetectorFlag,NULL,true); - for (int i=0;igetActor()); - - if (myactor) - { - - plPXPhysical* physical = (plPXPhysical*)myactor->userData; - if (physical) - { - plCollideMsg* msg = new plCollideMsg; - - msg->fOtherKey = fOwner; - msg->fEntering = entering; - msg->AddReceiver(physical->GetKey()); - msg->Send(); - } - } - } - - - } - -} - -////////////////////////////////////////////////////////////////////////// - - -class PXControllerHitReport : public NxUserControllerHitReport -{ -public: - virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) - { - plPXPhysicalController* ac = plPXPhysicalController::FindController(hit.controller); - - NxActor& actor = hit.shape->getActor(); - plPXPhysical* phys = (plPXPhysical*)actor.userData; - - static hsScalar SlopeLimit = kSLOPELIMIT; - hsVector3 normal = plPXConvert::Vector(hit.worldNormal); - hsScalar dot = normal * kAvatarUp; - if ( dot < SlopeLimit ) - ac->AddSlidingNormal(normal); - else - ac->GroundHit(); - -#ifndef PLASMA_EXTERNAL_RELEASE - plDbgCollisionInfo info; - info.fNormal = normal; - info.fSO = plSceneObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); - info.fOverlap = false; - NxShape* const *shapes = hit.controller->getActor()->getShapes(); - int numShapes = hit.controller->getActor()->getNbShapes(); - int i; - for (i = 0; i < numShapes; i++) - { - // should only be one capsule shape - const NxCapsuleShape *capShape = shapes[i]->isCapsule(); - if (capShape) - { - NxCapsule cap; - capShape->getWorldCapsule(cap); - if (hit.shape->checkOverlapCapsule(cap)) - info.fOverlap = true; - } - } - ac->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)) - { - // If this is the local avatar, we need to take ownership of this - // dynamic if we haven't already - if (ac->fLOSDB == plSimDefs::kLOSDBLocalAvatar && !phys->IsLocallyOwned() && - !phys->GetProperty(plSimulationInterface::kNoOwnershipChange)) - { - plSynchedObject* obj = plSynchedObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); - - obj->SetNetGroupConstant(plNetGroup::kNetGroupLocalPhysicals); - - // Tell all the other clients that we own this physical - plSetNetGroupIDMsg* setNetGroupID = new plSetNetGroupIDMsg; - setNetGroupID->fId = plNetGroup::kNetGroupRemotePhysicals; - setNetGroupID->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); - setNetGroupID->SetBCastFlag(plMessage::kLocalPropagate, false); - setNetGroupID->Send(obj->GetKey()); - } - - plSimulationMgr::GetInstance()->ConsiderSynch(phys, nil); - - hsVector3 dir = plPXConvert::Vector(hit.dir); - // We only allow horizontal pushes. Vertical pushes when we stand on - // dynamic objects creates useless stress on the solver. - if (dir.fZ < 0) - { - dir.fZ = 0; - dir.Normalize(); - } - - if (!dir.IsEmpty()) - { - static hsScalar kForceScale = 5.f; - //static hsScalar kForceScale = 4.f; - NxF32 coeff = actor.getMass() * hit.length * kForceScale; - hsPoint3 pos((hsScalar)hit.worldPos.x, (hsScalar)hit.worldPos.y, (hsScalar)hit.worldPos.z); - phys->SetHitForce((dir*coeff), pos); - } - } - } - else // else if the avatar hit a static - { - return NX_ACTION_NONE; - } - - if (phys && phys->GetProperty(plSimulationInterface::kAvAnimPushable)) - { - hsQuat inverseRotation = ac->fLocalRotation.Inverse(); - hsVector3 normal = plPXConvert::Vector(hit.worldNormal); - ac->fPushingPhysical = phys; - ac->fFacingPushingPhysical = (inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false); - } - - return NX_ACTION_NONE; - } - - virtual NxControllerAction onControllerHit(const NxControllersHit& hit) - { - return NX_ACTION_NONE; - } - -} gMyReport; - - -////////////////////////////////////////////////////////////////////////// - -const hsScalar plPXPhysicalController::kAirTimeThreshold = .1f; // seconds - -plPXPhysicalController::plPXPhysicalController(plKey ownerSO, hsScalar radius, hsScalar height) - : fOwner(ownerSO) - , fWorldKey(nil) - , fRadius(radius) - , fHeight(height) - , fController(nil) - , fLinearVelocity(0, 0, 0) - , fAngularVelocity(0) - , fAchievedLinearVelocity(0, 0, 0) - , fLocalPosition(0, 0, 0) - , fLocalRotation(0, 0, 0, 1) - , fEnable(true) - , fEnableChanged(false) - , fLOSDB(plSimDefs::kLOSDBNone) - , fGroundHit(false) - , fFalseGround(false) - , fTimeInAir(0) - , fPushingPhysical(nil) - , fFacingPushingPhysical(false) - , fProxyGen(nil) - , fKinematicActor(nil) - , fKinematic(false) - , fKinematicChanged(false) - , fKinematicEnableNextUpdate(false) - , fHitHead(false) -{ - gControllers.push_back(this); - fLastGlobalLoc.Reset(); - ICreateController(); - Enable(false); -} - -plPXPhysicalController::~plPXPhysicalController() -{ - IDeleteController(); - - for (int i = 0; i < gControllers.size(); i++) - { - if (gControllers[i] == this) - { - gControllers.erase(gControllers.begin()+i); - break; - } - } - - delete fProxyGen; -} - -// WARNING: If this is an armatureMod, it'll have its own idea about when -// physics should be enabled/disabled. Use plArmatureModBase::EnablePhysics() instead. -void plPXPhysicalController::Enable(bool enable) -{ - if (fEnable != enable) - { - fEnable = enable; - if (fEnable) - fEnableChanged = true; - else - { - // See ISendUpdates for why we don't re-enable right away - fController->setCollision(fEnable); - } - } -} - -void plPXPhysicalController::AddSlidingNormal(hsVector3 vec) -{ - // We get lots of duplicates, so check. - int i; - for (i = 0; i < fSlidingNormals.GetCount(); i++) - { - if (hsABS(fSlidingNormals[i].fX - vec.fX) <= .01 && - hsABS(fSlidingNormals[i].fY - vec.fY) <= .01 && - hsABS(fSlidingNormals[i].fZ - vec.fZ) <= .01) - { - return; - } - } - fSlidingNormals.Append(vec); -} - - -void plPXPhysicalController::IGetPositionSim(hsPoint3& pos) const -{ - const NxExtendedVec3& nxPos = fController->getPosition(); - pos.Set(hsScalar(nxPos.x), hsScalar(nxPos.y), hsScalar(nxPos.z) - kPhysZOffset); -} - -void plPXPhysicalController::SetSubworld(plKey world) -{ - if (fWorldKey != world) - { - bool wasEnabled = fEnable; -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - // PHYSX FIXME - before leaving this world, sending leaving detector events if we are inside a convex hull detector - hsPoint3 pos; - IGetPositionSim(pos); - plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,false); -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - //need to inform detectors in the old world that we are leaving - //IInformDetectors(false); - //done informing old world - - IDeleteController(); - fWorldKey = world; - ICreateController(); - if (wasEnabled) - Enable(false); - // need to disable the kinematic also so that it doesn't trip over random detector regions - fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); - hsMatrix44 globalLoc = fLastGlobalLoc; - if (GetSubworldCI()) - fPrevSubworldW2L = GetSubworldCI()->GetWorldToLocal(); - ISetGlobalLoc(globalLoc); - // we need to let the re-enable code put thing in order... so that 0,0,0 is not triggered and ISendUpdates do the enable and update detectors - if (wasEnabled) - Enable(true); - // and then re-enable the kinematic on the next update (ISendUpdates) - fKinematicEnableNextUpdate = true; - plPXPhysicalController::RebuildCache(); - } -} - -const plCoordinateInterface* plPXPhysicalController::GetSubworldCI() const -{ - if (fWorldKey) - { - plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); - if (so) - return so->GetCoordinateInterface(); - } - return nil; -} - -void plPXPhysicalController::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 * hsScalarPI) - zRot; // axis is backwards, so reverse the angle too - - pos = fLocalPosition; -} - -void plPXPhysicalController::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 plPXPhysicalController::ISetGlobalLoc(const hsMatrix44& l2w) -{ - fLastGlobalLoc = l2w; - - // Update our subworld position and rotation - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) - { - const hsMatrix44& w2s = fPrevSubworldW2L; - hsMatrix44 l2s = w2s * l2w; - - l2s.GetTranslate(&fLocalPosition); - fLocalRotation.SetFromMatrix44(l2s); - } - else - { - l2w.GetTranslate(&fLocalPosition); - fLocalRotation.SetFromMatrix44(l2w); - } - - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - if (fProxyGen) - fProxyGen->SetTransform(l2w, w2l); - - // Update the physical position - NxExtendedVec3 nxPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kPhysZOffset); - fController->setPosition(nxPos); - IMatchKinematicToController(); -} - -void plPXPhysicalController::IMatchKinematicToController() -{ - if ( fKinematicActor) - { - NxExtendedVec3 cPos = fController->getPosition(); - NxVec3 prevKinPos = fKinematicActor->getGlobalPosition(); - NxVec3 kinPos; - kinPos.x = (NxReal)cPos.x; - kinPos.y = (NxReal)cPos.y; - kinPos.z = (NxReal)cPos.z; - if (plSimulationMgr::fExtraProfile) - SimLog("Match setting kinematic from %f,%f,%f to %f,%f,%f",prevKinPos.x,prevKinPos.y,prevKinPos.z,kinPos.x,kinPos.y,kinPos.z ); - fKinematicActor->setGlobalPosition(kinPos); - } -} - -void plPXPhysicalController::IMoveKinematicToController(hsPoint3& pos) -{ - if ( fKinematicActor) - { - NxVec3 kinPos = fKinematicActor->getGlobalPosition(); - if ( abs(kinPos.x-pos.fX) + abs(kinPos.y-pos.fY) + (abs(kinPos.z-pos.fZ-kPhysZOffset)) > 0.0001f) - { - NxVec3 newPos; - newPos.x = (NxReal)pos.fX; - newPos.y = (NxReal)pos.fY; - newPos.z = (NxReal)pos.fZ+kPhysZOffset; - if (fEnable || fKinematic) - { - if (plSimulationMgr::fExtraProfile) - SimLog("Moving kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); - // use the position - fKinematicActor->moveGlobalPosition(newPos); - } - else - { - if (plSimulationMgr::fExtraProfile) - SimLog("Setting kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); - fKinematicActor->setGlobalPosition(newPos); - } - } - } -} - -void plPXPhysicalController::ISetKinematicLoc(const hsMatrix44& l2w) -{ - - hsPoint3 kPos; - // Update our subworld position and rotation - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) - { - const hsMatrix44& w2s = subworldCI->GetWorldToLocal(); - hsMatrix44 l2s = w2s * l2w; - - l2s.GetTranslate(&kPos); - } - else - { - l2w.GetTranslate(&kPos); - } - - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - if (fProxyGen) - fProxyGen->SetTransform(l2w, w2l); - - // add z offset - kPos.fZ += kPhysZOffset; - // Update the physical position of kinematic - if (fEnable || fKinematic) - fKinematicActor->moveGlobalPosition(plPXConvert::Point(kPos)); - else - fKinematicActor->setGlobalPosition(plPXConvert::Point(kPos)); -} - - -void plPXPhysicalController::Kinematic(bool state) -{ - if (fKinematic != state) - { - fKinematic = state; - if (fKinematic) - { - // See ISendUpdates for why we don't re-enable right away - fController->setCollision(false); -#ifdef PHYSX_KINEMATIC_IS_DISABLED - fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); -#endif - } - else - { - fKinematicChanged = true; - } - } -} - -bool plPXPhysicalController::IsKinematic() -{ - if (fKinematicActor) - { -#ifdef PHYSX_KINEMATIC_IS_DISABLED - if (!fKinematicActor->readActorFlag(NX_AF_DISABLE_COLLISION)) - return true; -#else - return fKinematic; -#endif - } - return false; -} - -void plPXPhysicalController::GetKinematicPosition(hsPoint3& pos) -{ - pos.Set(-1,-1,-1); - if ( fKinematicActor ) - { - NxVec3 klPos = fKinematicActor->getGlobalPosition(); - pos.Set(hsScalar(klPos.x), hsScalar(klPos.y), hsScalar(klPos.z) - kPhysZOffset); - } -} - - -void plPXPhysicalController::IApply(hsScalar delSecs) -{ - /*static const uint32_t collideFlags = - 1<ObjectIsLoaded()); - if (so) - { - // If we've been moved since the last physics update (somebody warped us), - // update the physics before we apply velocity. - const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); - if (!CompareMatrices(l2w, fLastGlobalLoc, .0001f)) - { - ISetKinematicLoc(l2w); - } - } - // then jump out - return; - } - - if (!fEnable) - return; - - bool gotGroundHit = fGroundHit; - fGroundHit = false; - - fPushingPhysical = nil; - fFacingPushingPhysical = false; - - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) - { - // If we've been moved since the last physics update (somebody warped us), - // update the physics before we apply velocity. - const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); - if (!CompareMatrices(l2w, fLastGlobalLoc, .0001f)) - ISetGlobalLoc(l2w); - - // Convert our avatar relative velocity to subworld relative - if (!fLinearVelocity.IsEmpty()) - { - fLinearVelocity = l2w * fLinearVelocity; - - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) - fLinearVelocity = subworldCI->GetWorldToLocal() * fLinearVelocity; - } - - // Add in gravity if the avatar's z velocity isn't being set explicitly - // (Add in a little fudge factor, since the animations usually add a - // tiny bit of z.) - if (hsABS(fLinearVelocity.fZ) < 0.001f) - { - static const float kGravity = -32.f; - - // Get our previous z velocity. If we're on the ground, clamp it to zero at - // the largest, so we won't launch into the air if we're running uphill. - hsScalar prevZVel = fAchievedLinearVelocity.fZ; - if (IsOnGround()) - prevZVel = hsMinimum(prevZVel, 0.f); - - hsScalar grav = kGravity * delSecs; - - // If our gravity contribution isn't high enough this frame, we won't - // report a collision even when standing on solid ground. - hsScalar maxGrav = -.001f / delSecs; - if (grav > maxGrav) - grav = maxGrav; - - fLinearVelocity.fZ = prevZVel + grav; - - // Technically this is nonsensical and wrong, capping our velocity to - // an accelleration constant. But no one seems to really mind. - if (fLinearVelocity.fZ < kGravity) - fLinearVelocity.fZ = kGravity; - } - - // If we're airborne and the velocity isn't set, use the velocity from - // the last frame so we maintain momentum. - if (!IsOnGround() && fLinearVelocity.fX == 0.f && fLinearVelocity.fY == 0.f) - { - fLinearVelocity.fX = fAchievedLinearVelocity.fX; - fLinearVelocity.fY = fAchievedLinearVelocity.fY; - } - - if (!IsOnGround() || IsOnFalseGround()) - { - // We're not on solid ground, so we should be sliding against whatever - // we're hitting (like a rock cliff). Each vector in fSlidingNormals is - // the surface normal of a collision that's too steep to be ground, so - // we project our current velocity onto that plane and slide along the - // wall. - // - // Also, sometimes PhysX reports a bunch of collisions from the wall, - // but nothing from underneath (when there should be). So if we're not - // touching ground, we offset the avatar in the direction of the - // surface normal(s). This doesn't fix the issue 100%, but it's a hell - // of a lot better than nothing, and suitable duct tape until a future - // PhysX revision fixes the issue. - // - // Yes, there's room for optimization here if we care. - hsVector3 offset(0.f, 0.f, 0.f); - for (i = 0; i < fSlidingNormals.GetCount(); i++) - { - offset += fSlidingNormals[i]; - - hsVector3 velNorm = fLinearVelocity; - if (velNorm.MagnitudeSquared() > 0) - velNorm.Normalize(); - - if (velNorm * fSlidingNormals[i] < 0) - { - hsVector3 proj = (velNorm % fSlidingNormals[i]) % fSlidingNormals[i]; - if (velNorm * proj < 0) - proj *= -1.f; - - fLinearVelocity = fLinearVelocity.Magnitude() * proj; - } - } - if (offset.MagnitudeSquared() > 0) - { - // 5 ft/sec is roughly the speed we walk backwards. - // The higher the value, the less likely you'll trip - // the bug, and this seems reasonable. - offset.Normalize(); - fLinearVelocity += offset * 5; - } - } - - // Scale the velocity to our actual step size (by default it's feet/sec) - NxVec3 vel(fLinearVelocity.fX * delSecs, fLinearVelocity.fY * delSecs, fLinearVelocity.fZ * delSecs); - NxU32 colFlags = 0; - - fGroundHit = false; - fFalseGround = false; - fSlidingNormals.Swap(fPrevSlidingNormals); - fSlidingNormals.SetCount(0); - -#ifndef PLASMA_EXTERNAL_RELEASE - fDbgCollisionInfo.SetCount(0); -#endif // PLASMA_EXTERNAL_RELEASE - - fController->move(vel, collideFlags, 0.0001, colFlags); - - - ICheckForFalseGround(); - /*If the Physx controller thinks we have a collision from below, need to make sure we - have at least have false ground, otherwise Autostepping can send us into the air, and we will some times - float/panic link. For some reason the NxControllerHitReport does not always send messages - regarding Controller contact with ground plane, but will (almost) always return NXCC_COLLISION_DOWN - with the move method. - */ - if(((colFlags&NXCC_COLLISION_DOWN )==NXCC_COLLISION_DOWN )&&(fGroundHit==false)) - { - fFalseGround=true; - } - /* - The top sphere half was hit, but the ControllerHit Report doesn't know - In IUpdate fHitHead will be used to keep from gaining unrealistic velocity in the x&y Direction - */ - if(colFlags&NXCC_COLLISION_UP) - { - fHitHead=true; - } - -#ifndef PLASMA_EXTERNAL_RELEASE - if (fDebugDisplay) - IDrawDebugDisplay(); -#endif // PLASMA_EXTERNAL_RELEASE - } -} - -void plPXPhysicalController::ISendUpdates(hsScalar delSecs) -{ - if (!fEnable || fKinematic) - { - // When we're in non-phys or a behavior we can't go through the rest of the function - // so we need to get out early, but we need to update the current position if we're - // in a subworld. - - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - const plCoordinateInterface* ci = GetSubworldCI(); - if (ci && so) - { - const hsMatrix44& soL2W = so->GetCoordinateInterface()->GetLocalToWorld(); - const hsMatrix44& ciL2W = ci->GetLocalToWorld(); - - hsMatrix44 l2w = fPrevSubworldW2L * soL2W; - l2w = ciL2W * l2w; - - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - - ((plCoordinateInterface*)so->GetCoordinateInterface())->SetTransform(l2w, w2l); - ((plCoordinateInterface*)so->GetCoordinateInterface())->FlushTransform(); - - ISetGlobalLoc(l2w); - } - - return; - } - - // PhysX loves to cache stuff. However, it doesn't like to update it (see - // the RebuildCache crap above). Say the avatar is disabled and sitting at - // point 0,0,0. We warp him to some other position and enable him. If you - // do the enable before the sim step is done, regardless of whether you move - // him first, he will send out a penetration with any detector at 0,0,0. As - // far as I can tell there's no way around this, and I tried a lot of things. - // The only solution I found is to move the avatar, run the sim step, then - // enable him. This means he won't trigger any detectors at his new position - // until the next frame, but hopefully that won't be too noticeable. - if (fEnableChanged) - { - fEnableChanged = false; - fController->setCollision(fEnable); -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - // PHYSX FIXME - after re-enabling check to see if we are inside any convex hull detector regions - hsPoint3 pos; - IGetPositionSim(pos); - plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,fEnable); -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - //IInformDetectors(true); - } - if (fKinematicChanged) - { - fKinematicChanged = false; - fController->setCollision(true); -#ifdef PHYSX_KINEMATIC_IS_DISABLED - fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); -#endif // PHYSX_KINEMATIC_IS_DISABLED - } - if (fKinematicEnableNextUpdate) - { - fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); - fKinematicEnableNextUpdate = false; - } - - if (!fGroundHit && !fFalseGround) - fTimeInAir += delSecs; - else - fTimeInAir = 0.f; - - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) - { - // Get the current position of the physical - hsPoint3 curLocalPos; - IGetPositionSim(curLocalPos); - IMoveKinematicToController(curLocalPos); - - { - const hsMatrix44& w2l = so->GetCoordinateInterface()->GetLocalToWorld(); - fAchievedLinearVelocity = hsVector3(curLocalPos - fLocalPosition); - fAchievedLinearVelocity /= delSecs; - } - /*if we hit our head the sweep api might try to - move us laterally to go as high as we requested kind of like autostep, to top it off the - way the NxCharacter and the sweep api work as a whole NxControllerHitReport::OnShapeHit - wont be called regarding the head blow. - if we are airborne: with out this we will gain large amounts of velocity in the x y plane - and on account of fAchievedLinearVelocity being used in the next step we will fly sideways - */ - bool headhit=fHitHead; - fHitHead=false; - if(headhit&&!(fGroundHit||fFalseGround)) - { - //we have hit our head and we don't have anything beneath our feet - //not really friction just a way to make it seem more realistic keep between 0 and 1 - hsScalar headFriction=0.0; - fAchievedLinearVelocity.fX=(1.0-headFriction)*fLinearVelocity.fX; - fAchievedLinearVelocity.fY=(1.0-headFriction)*fLinearVelocity.fY; - //only clamping when hitting head and going upwards, if going down leave it be - // this should only occur when going down stairwells with low ceilings like in cleft - //kitchen area - if(fAchievedLinearVelocity.fZ>0.0) - { - fAchievedLinearVelocity.fZ=0.0; - } - - } - - // Apply angular velocity - if (fAngularVelocity != 0.f) - { - hsScalar angle; - hsVector3 axis; - fLocalRotation.NormalizeIfNeeded(); - fLocalRotation.GetAngleAxis(&angle, &axis); - - // adjust it (quaternions are weird...) - if (axis.fZ < 0) - angle = (2*hsScalarPI) - angle; // axis is backwards, so reverse the angle too - - angle += fAngularVelocity * delSecs; - - // make sure we wrap around - if (angle < 0) - angle = (2*hsScalarPI) + angle; // angle is -, so this works like a subtract - if (angle >= (2*hsScalarPI)) - angle = angle - (2*hsScalarPI); - - // and set the new angle - fLocalRotation.SetAngleAxis(angle, hsVector3(0,0,1)); - } - - // We can't only send updates when the physical position changes because the - // world relative position may be changing all the time if we're in a subworld. -// if (curLocalPos != fLocalPosition || fAngularVelocity != 0.f) - { - fLocalPosition = curLocalPos; - - // Apply rotation and translation - fLocalRotation.MakeMatrix(&fLastGlobalLoc); - fLastGlobalLoc.SetTranslate(&fLocalPosition); - - // Localize to global coords if in a subworld - const plCoordinateInterface* ci = GetSubworldCI(); - if (ci) - { - const hsMatrix44& l2w = ci->GetLocalToWorld(); - fLastGlobalLoc = l2w * fLastGlobalLoc; - } - - plCorrectionMsg* corrMsg = new plCorrectionMsg; - corrMsg->fLocalToWorld = fLastGlobalLoc; - corrMsg->fLocalToWorld.GetInverse(&corrMsg->fWorldToLocal); - corrMsg->fDirtySynch = true; - - hsMatrix44 w2l; - fLastGlobalLoc.GetInverse(&w2l); - //if (fProxyGen) - // fProxyGen->SetTransform(fLastGlobalLoc, w2l); - - // Send the new position to the plArmatureMod and the scene object - const plArmatureMod* armMod = plArmatureMod::ConvertNoRef(so->GetModifierByType(plArmatureMod::Index())); - if (armMod) - corrMsg->AddReceiver(armMod->GetKey()); - corrMsg->AddReceiver(fOwner); - - corrMsg->Send(); - } - } - - fLinearVelocity.Set(0, 0, 0); - fAngularVelocity = 0; -} - -void plPXPhysicalController::ICheckForFalseGround() -{ - if (fGroundHit) - return; // Already collided with "real" ground. - - // We need to check for the case where the avatar hasn't collided with "ground", but is colliding - // with a few other objects so that he's not actually falling (wedged in between some slopes). - // We do this by answering the following question (in 2d top-down space): "If you sort the contact - // normals by angle, is there a large enough gap between normals?" - // - // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. - // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be - // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap - // larger than 180 degrees between sorted normals. - // - // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he - // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've - // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. - // This is the same running into 2 walls where the angle between them is 60. - int i, j; - const hsScalar threshold = hsScalarDegToRad(240); - int numContacts = fSlidingNormals.GetCount() + fPrevSlidingNormals.GetCount(); - if (numContacts >= 2) - { - // For extra fun... PhysX will actually report some collisions every other frame, as though - // we're bouncing back and forth between the two (or more) objects blocking us. So it's not - // enough to look at this frame's collisions, we have to check previous frames too. - hsTArray fCollisionAngles; - fCollisionAngles.SetCount(numContacts); - int angleIdx = 0; - for (i = 0; i < fSlidingNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = hsATan2(fSlidingNormals[i].fY, fSlidingNormals[i].fX); - } - for (i = 0; i < fPrevSlidingNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = hsATan2(fPrevSlidingNormals[i].fY, fPrevSlidingNormals[i].fX); - } - - // numContacts is rarely larger than 6, so let's do a simple bubble sort. - for (i = 0; i < numContacts; i++) - { - for (j = i + 1; j < numContacts; j++) - { - if (fCollisionAngles[i] > fCollisionAngles[j]) - { - hsScalar tempAngle = fCollisionAngles[i]; - fCollisionAngles[i] = fCollisionAngles[j]; - fCollisionAngles[j] = tempAngle; - } - } - } - - // sorted, now we check. - for (i = 1; i < numContacts; i++) - { - if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) - break; - } - - if (i == numContacts) - { - // We got to the end. Check the last with the first and make your decision. - if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * hsScalarPI))) - fFalseGround = true; - } - } -} - -void plPXPhysicalController::ICreateController() -{ - NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); - - NxCapsuleControllerDesc desc; - desc.position.x = 0; - desc.position.y = 0; - desc.position.z = 0; - desc.upDirection = NX_Z; - desc.slopeLimit = kSLOPELIMIT; - desc.skinWidth = kPhysxSkinWidth; - desc.stepOffset = STEP_OFFSET; - desc.callback = &gMyReport; - desc.userData = this; - desc.radius = fRadius; - desc.height = fHeight; - desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; - //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; - fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); - - // Change the avatars shape groups. The avatar doesn't actually use these when - // it's determining collision, but if you're standing still and an object runs - // into you, it'll pass through without this. - NxActor* actor = fController->getActor(); - NxShape* shape = actor->getShapes()[0]; - shape->setGroup(plSimDefs::kGroupAvatar); - - // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. - NxActorDesc actorDesc; - NxCapsuleShapeDesc capDesc; - capDesc.radius = fRadius; - capDesc.height = fHeight; - capDesc.group = plSimDefs::kGroupAvatar; - actorDesc.shapes.pushBack(&capDesc); - NxBodyDesc bodyDesc; - bodyDesc.mass = 1.f; - actorDesc.body = &bodyDesc; - bodyDesc.flags |= NX_BF_KINEMATIC; - actorDesc.name = "AvatarTriggerKinematicGuy"; - fSeeking=false; - try - { - fKinematicActor = scene->createActor(actorDesc); - } catch (...) - { - hsAssert(false, "Actor creation crashed"); - } -#ifdef PHYSX_KINEMATIC_IS_DISABLED - // initially start as in-active - fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); -#endif - // set the matrix to be the same as the controller's actor... that should orient it to be the same - fKinematicActor->setGlobalPose(actor->getGlobalPose()); - - // the proxy for the debug display - //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); - - hsColorRGBA physColor; - hsScalar opac = 1.0f; - - // local avatar is light purple and transparent - physColor.Set(.2f, .1f, .2f, 1.f); - opac = 0.8f; - - // the avatar proxy doesn't seem to work... not sure why? - fProxyGen = new plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); - fProxyGen->Init(this); -} - -void plPXPhysicalController::IDeleteController() -{ - if (fController) - { - gControllerMgr.releaseController(*fController); - fController = nil; - - if (fKinematicActor) - { - NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); - scene->releaseActor(*fKinematicActor); - fKinematicActor = nil; - } - plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); - } -} - -// Make a visible object that can be viewed by users for debugging purposes. -plDrawableSpans* plPXPhysicalController::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) -{ - 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; -} - -#ifndef PLASMA_EXTERNAL_RELEASE - -void plPXPhysicalController::IDrawDebugDisplay() -{ - plDebugText &debugTxt = plDebugText::Instance(); - char strBuf[ 2048 ]; - int lineHeight = debugTxt.GetFontSize() + 4; - uint32_t scrnWidth, scrnHeight; - - debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); - int y = 10; - int x = 10; - - sprintf(strBuf, "Controller Count: %d", gControllers.size()); - debugTxt.DrawString(x, y, strBuf); - y += lineHeight; - - debugTxt.DrawString(x, y, "Avatar Collisions:"); - y += lineHeight; - - int i; - for (i = 0; i < fDbgCollisionInfo.GetCount(); i++) - { - hsVector3 normal = fDbgCollisionInfo[i].fNormal; - char *overlapStr = fDbgCollisionInfo[i].fOverlap ? "yes" : "no"; - hsScalar angle = hsScalarRadToDeg(hsACosine(normal * hsVector3(0, 0, 1))); - sprintf(strBuf, " Obj: %s, Normal: (%.2f, %.2f, %.2f), Angle(%.1f), Overlap(%3s)", - fDbgCollisionInfo[i].fSO->GetKeyName(), - normal.fX, normal.fY, normal.fZ, angle, overlapStr); - debugTxt.DrawString(x, y, strBuf); - y += lineHeight; - } -} - -#endif PLASMA_EXTERNAL_RELEASE diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h deleted file mode 100644 index be03fabe..00000000 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h +++ /dev/null @@ -1,211 +0,0 @@ -/*==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 . - -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==*/ -#ifndef plPXPhysicalController_h_inc -#define plPXPhysicalController_h_inc - -#include "plAvatar/plAvCallbackAction.h" -#include "hsQuat.h" - -#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 - -class NxController; -class NxCapsuleController; -class NxActor; -class plCoordinateInterface; -class plPhysicalProxy; -class plDrawableSpans; -class hsGMaterial; -class NxCapsule; -#ifndef PLASMA_EXTERNAL_RELEASE -class plDbgCollisionInfo -{ -public: - plSceneObject *fSO; - hsVector3 fNormal; - bool fOverlap; -}; -#endif // PLASMA_EXTERNAL_RELEASE - -class plPXPhysicalController : public plPhysicalController -{ -public: - plPXPhysicalController(plKey ownerSO, hsScalar height, hsScalar radius); - virtual ~plPXPhysicalController(); - - virtual void Enable(bool enable); - virtual bool IsEnabled() const { return fEnable; } - - virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } - plSimDefs::plLOSDB GetLOSDB() const { return fLOSDB; } - - virtual void SetVelocities(const hsVector3& linearVel, hsScalar angVel) - { - fLinearVelocity = linearVel; - fAngularVelocity = angVel; - } - - virtual const hsVector3& GetLinearVelocity() const { return fAchievedLinearVelocity; } - virtual void ResetAchievedLinearVelocity() { fAchievedLinearVelocity.Set(0.f, 0.f, 0.f); } - - virtual plKey GetSubworld() const { return fWorldKey; } - virtual void SetSubworld(plKey world); - - virtual bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } - virtual bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } - virtual void GroundHit() { fGroundHit = true; } - virtual hsScalar GetAirTime() const { return fTimeInAir; } - virtual void ResetAirTime() { fTimeInAir = 0.f; } - virtual void AddSlidingNormal(hsVector3 vec); - virtual hsTArray* GetSlidingNormals() { return &fSlidingNormals; } - - virtual plPhysical* GetPushingPhysical() const { return fPushingPhysical; } - virtual bool GetFacingPushingPhysical() const { return fFacingPushingPhysical; } - - virtual const plCoordinateInterface* GetSubworldCI() const; - - virtual void GetState(hsPoint3& pos, float& zRot); - virtual void SetState(const hsPoint3& pos, float zRot); - - plKey GetOwner() const { return fOwner; } - - // Called by the simulation mgr each frame - static void Update(bool prestep, hsScalar delSecs); - // Used by the LOS mgr to find the controller for an actor it hit - static plPXPhysicalController* GetController(NxActor& actor, bool* isController); - // test to see if there are any controllers (i.e. avatars) in this subworld - static bool plPXPhysicalController::AnyControllersInThisWorld(plKey world); - static int plPXPhysicalController::NumControllers(); - static int plPXPhysicalController::GetControllersInThisSubWorld(plKey world, int maxToReturn, - plPXPhysicalController** bufferout); - static int plPXPhysicalController::GetNumberOfControllersInThisSubWorld(plKey world); - // Call this if a static physical in the scene has changed (unloaded, - // collision enabled/disabled, etc) - static void RebuildCache(); - - virtual void GetPositionSim(hsPoint3& pos) const { IGetPositionSim(pos); } - - virtual void Kinematic(bool state); - virtual bool IsKinematic(); - virtual void GetKinematicPosition(hsPoint3& pos); - - virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); - - virtual const hsMatrix44& GetPrevSubworldW2L() { return fPrevSubworldW2L; } - - virtual void SetSeek(bool seek){fSeeking=seek;} - virtual void GetWorldSpaceCapsule(NxCapsule& cap); -#ifndef PLASMA_EXTERNAL_RELEASE - static bool fDebugDisplay; -#endif // PLASMA_EXTERNAL_RELEASE - -protected: - static const hsScalar kAirTimeThreshold; - - friend class PXControllerHitReport; - static plPXPhysicalController* FindController(NxController* controller); - - void IApply(hsScalar delSecs); - void ISendUpdates(hsScalar delSecs); - void ICheckForFalseGround(); - void ISetGlobalLoc(const hsMatrix44& l2w); - void IMatchKinematicToController(); - void IMoveKinematicToController(hsPoint3& pos); - void ISetKinematicLoc(const hsMatrix44& l2w); - void IGetPositionSim(hsPoint3& pos) const; - - void ICreateController(); - void IDeleteController(); - - void IInformDetectors(bool entering); - - plKey fOwner; - plKey fWorldKey; - hsScalar fRadius, fHeight; - NxCapsuleController* fController; - - // this is the kinematic actor for triggering things when the avatar is collision-less during behaviors - NxActor* fKinematicActor; - - hsVector3 fLinearVelocity; - hsScalar fAngularVelocity; - - hsVector3 fAchievedLinearVelocity; - - // The global position and rotation of the avatar last time we set it (so we - // can detect if someone else moves him) - hsMatrix44 fLastGlobalLoc; - // - hsPoint3 fLocalPosition; - hsQuat fLocalRotation; - - hsMatrix44 fPrevSubworldW2L; - - bool fEnable; - bool fEnableChanged; - plSimDefs::plLOSDB fLOSDB; - - bool fKinematic; - bool fKinematicChanged; - bool fKinematicEnableNextUpdate; - - bool fGroundHit; - bool fFalseGround; - hsScalar fTimeInAir; - hsTArray fSlidingNormals; - hsTArray fPrevSlidingNormals; - -#ifndef PLASMA_EXTERNAL_RELEASE - hsTArray fDbgCollisionInfo; - void IDrawDebugDisplay(); -#endif // PLASMA_EXTERNAL_RELEASE - - plPhysical* fPushingPhysical; - bool fFacingPushingPhysical; - - plPhysicalProxy* fProxyGen; // visual proxy for debugging - - bool fHitHead; - - bool fSeeking; -}; - -#endif // plPXPhysicalController_h_inc diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp index cdb1b968..edbb6808 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp @@ -40,13 +40,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plPXPhysicalControllerCore.h" -#include "plgDispatch.h" #include "plSimulationMgr.h" #include "plPXPhysical.h" #include "plPXConvert.h" #include "pnSceneObject/plSimulationInterface.h" #include "pnSceneObject/plSceneObject.h" -#include "pnMessage/plCorrectionMsg.h" #include "plAvatar/plArmatureMod.h" #include "pnSceneObject/plCoordinateInterface.h" #include "plDrawable/plDrawableGenerator.h" @@ -54,130 +52,100 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnMessage/plSetNetGroupIDMsg.h" #include "plMessage/plCollideMsg.h" #include "plModifier/plDetectorLog.h" -//#include "NxVecExtendedVec3.h" + +#include "plSurface/hsGMaterial.h" // For our proxy +#include "plSurface/plLayerInterface.h" // For our proxy #include "NxPhysics.h" -#include "ControllerManager.h" #include "NxCapsuleController.h" #include "NxCapsuleShape.h" +#include "ControllerManager.h" -#include "plSurface/hsGMaterial.h" -#include "plSurface/plLayerInterface.h" - - -#ifndef PLASMA_EXTERNAL_RELEASE -#include "plPipeline/plDebugText.h" -#endif - -#define kPhysxSkinWidth 0.1f -#define kPhysZOffset ((fRadius + (fHeight / 2)) + kPhysxSkinWidth) -#define kPhysicalHeightFudge 0.0f -#define STEP_OFFSET 1.f -#define kAvatarMass 160.f - +static ControllerManager gControllerMgr; +static std::vector gControllers; +static bool gRebuildCache = false; #ifndef PLASMA_EXTERNAL_RELEASE bool plPXPhysicalControllerCore::fDebugDisplay = false; #endif // PLASMA_EXTERNAL_RELEASE int plPXPhysicalControllerCore::fPXControllersMax = 0; -static ControllerManager gControllerMgr; -static std::vector gControllers; -static bool gRebuildCache=false; +#define kCCTSkinWidth 0.1f +#define kCCTStepOffset 0.6f +#define kCCTZOffset ((fRadius + (fHeight / 2)) + kCCTSkinWidth) +#define kPhysHeightCorrection 0.8f +#define kPhysZOffset ((kCCTZOffset + (kPhysHeightCorrection / 2)) - 0.05f) +#define kAvatarMass 200.0f -class PXControllerHitReportWalk : public NxUserControllerHitReport +static class PXControllerHitReport : public NxUserControllerHitReport { public: virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) { - plPXPhysicalControllerCore* ac = plPXPhysicalControllerCore::FindController(hit.controller); + plPXPhysicalControllerCore* controller = (plPXPhysicalControllerCore*)hit.controller->getUserData(); NxActor& actor = hit.shape->getActor(); plPXPhysical* phys = (plPXPhysical*)actor.userData; hsVector3 normal = plPXConvert::Vector(hit.worldNormal); - ac->fMovementInterface->IAddContactNormals(normal); #ifndef PLASMA_EXTERNAL_RELEASE plDbgCollisionInfo info; info.fNormal = normal; info.fSO = plSceneObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); - info.fOverlap = false; - NxShape* const *shapes = hit.controller->getActor()->getShapes(); - int numShapes = hit.controller->getActor()->getNbShapes(); - int i; - for (i = 0; i < numShapes; i++) - { - // should only be one capsule shape - const NxCapsuleShape *capShape = shapes[i]->isCapsule(); - if (capShape) - { - NxCapsule cap; - capShape->getWorldCapsule(cap); - if (hit.shape->checkOverlapCapsule(cap)) - info.fOverlap = true; - } - } - ac->fDbgCollisionInfo.Append(info); + + 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. - hsVector3 dir = plPXConvert::Vector(hit.dir); - float dirdotup=dir.fZ; - hsPoint3 pos((float)hit.worldPos.x, (float)hit.worldPos.y, (float)hit.worldPos.z); - NxExtendedVec3 controllerPos=hit.controller->getPosition(); - hsVector3 bottomOfTheCapsule((float)controllerPos.x,(float)controllerPos.y,(float)controllerPos.z); - bottomOfTheCapsule.fZ=bottomOfTheCapsule.fZ-(ac->fHeight/2.0f + ac->fRadius); - if (actor.isDynamic() && phys ) + if (actor.isDynamic()) { - if((hit.worldPos.z- bottomOfTheCapsule.fZ)<=ac->fRadius)//bottom hemisphere + if (!actor.readBodyFlag(NX_BF_KINEMATIC) && !actor.readBodyFlag(NX_BF_FROZEN)) { - // If this is an animated physical, we can stand on it - if (phys->GetProperty(plSimulationInterface::kPhysAnim)) + // Don't apply force when standing on top of an object. + if (normal.fZ < 0.85f) { - if(normal.fZ>=0) + hsVector3 velocity = controller->GetLinearVelocity(); + velocity.fZ = 0.0f; + float length = velocity.Magnitude(); + if (length > 0) { - //we consider this ground - ac->fMovementInterface->AddOnTopOfObject(phys); + // 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); } } } - if ( !actor.readBodyFlag(NX_BF_KINEMATIC) && !actor.readBodyFlag(NX_BF_FROZEN)) - { - // If this is the local avatar, we need to take ownership of this - // dynamic if we haven't already - if (ac->fLOSDB == plSimDefs::kLOSDBLocalAvatar && !phys->IsLocallyOwned() && - !phys->GetProperty(plSimulationInterface::kNoOwnershipChange)) - { - plSynchedObject* obj = plSynchedObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); - obj->SetNetGroupConstant(plNetGroup::kNetGroupLocalPhysicals); - // Tell all the other clients that we own this physical - plSetNetGroupIDMsg* setNetGroupID = new plSetNetGroupIDMsg; - setNetGroupID->fId = plNetGroup::kNetGroupRemotePhysicals; - setNetGroupID->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); - setNetGroupID->SetBCastFlag(plMessage::kLocalPropagate, false); - setNetGroupID->Send(obj->GetKey()); - } - plSimulationMgr::GetInstance()->ConsiderSynch(phys, nil); - - // Now, let's impart some force, fool. - // Note: This is a VERY simple implementation. The old one was too hacky and caused weird stuff to happen. - hsVector3 acceleration = (ac->GetLinearVelocity() - plPXConvert::Vector(actor.getLinearVelocity())); - hsVector3 force2impart = acceleration * kAvatarMass; - // Bad things happen if we impart force directly on top of the actor, so let's allow the avatar to run - // over those physicals and not break them. This is mostly an issue with stuff smaller than step size. - if (!force2impart.IsEmpty() && normal.fZ < .90f) - phys->SetHitForce(force2impart, pos); - } } else // else if the avatar hit a static { + controller->fMovementStrategy->AddContactNormals(normal); return NX_ACTION_NONE; } - if (phys->GetProperty(plSimulationInterface::kAvAnimPushable)) + if (phys && phys->GetProperty(plSimulationInterface::kAvAnimPushable)) { - hsQuat inverseRotation = ac->fLocalRotation.Inverse(); + hsQuat inverseRotation = controller->fLocalRotation.Inverse(); hsVector3 normal = plPXConvert::Vector(hit.worldNormal); - ac->SetPushingPhysical( phys); - ac->SetFacingPushingPhysical((inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false)); + controller->SetPushingPhysical(phys); + controller->SetFacingPushingPhysical((inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false)); } return NX_ACTION_NONE; } @@ -185,161 +153,34 @@ public: { return NX_ACTION_NONE; } - -} gMyReport; - +} gControllerHitReport; plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, float height, float width) { - // Test to see how many controller there already is - if ( !plPXPhysicalControllerCore::fPXControllersMax || plPXPhysicalControllerCore::NumControllers() < plPXPhysicalControllerCore::fPXControllersMax ) - { - float radius = width / 2.f; - float realHeight = height - width + kPhysicalHeightFudge; - return new plPXPhysicalControllerCore(ownerSO, realHeight,radius); - } - return nil; -} - -//Static Helper Func -plPXPhysicalControllerCore* plPXPhysicalControllerCore::FindController(NxController* controller) -{ - for (int i = 0; i < gControllers.size(); i++) + if (!plPXPhysicalControllerCore::fPXControllersMax || gControllers.size() < plPXPhysicalControllerCore::fPXControllersMax) { - plPXPhysicalControllerCore* ac = gControllers[i]; - if (ac->fController == controller) - return ac; + float radius = width / 2.0f; + float realHeight = height - width; + return new plPXPhysicalControllerCore(ownerSO, realHeight, radius); } return nil; } -void plPXPhysicalControllerCore::RebuildCache(){gRebuildCache=true;} - -plPXPhysicalControllerCore* plPXPhysicalControllerCore::GetController(NxActor& actor, bool* isController) -{ - *isController = false; - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalControllerCore* ac = gControllers[i]; - if (ac->fController && ac->fController->getActor() == &actor) - { - *isController = true; - return ac; - } - if ( ac->fKinematicActor == &actor) - { - return ac; - } - } - - return nil; -} -void plPXPhysicalControllerCore::GetWorldSpaceCapsule(NxCapsule& cap) const -{ - if(this->fKinematicActor) - { - int numshapes=fKinematicActor->getNbShapes(); - if (numshapes==1) - { - //there should only be one shape on a controller - NxShape* const *shapes=fKinematicActor->getShapes(); - //and since it is a capsule controller it better be a capsule; - NxCapsuleShape *capShape = shapes[0]->isCapsule(); - if(capShape) capShape->getWorldCapsule(cap); - } - } -} -bool plPXPhysicalControllerCore::AnyControllersInThisWorld(plKey world) -{ - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalControllerCore* ac = gControllers[i]; - if (ac->GetSubworld() == world) - return true; - } - return false; -} - -int plPXPhysicalControllerCore::NumControllers() -{ - return gControllers.size(); -} -int plPXPhysicalControllerCore::GetControllersInThisSubWorld(plKey world, int maxToReturn,plPXPhysicalControllerCore** bufferout) -{ - int i=0; - for (int j=0;jGetSubworld()==world) - { - if(iGetSubworld()==world)i++; - } - return i; -} -// plPXPhysicalControllerCore::plPXPhysicalControllerCore(plKey ownerSO, float height, float radius) - : plPhysicalControllerCore(ownerSO,height,radius) - , fController(nil) - , fProxyGen(nil) - , fKinematicActor(nil) - ,fPreferedRadius(radius) - ,fPreferedHeight(height) - , fBehavingLikeAnimatedPhys(true) -{ - fLocalPosition.Set(0, 0, 0); - fLocalRotation.Set(0, 0, 0, 1); + : plPhysicalControllerCore(ownerSO, height, radius), + fController(nil), + fActor(nil), + fProxyGen(nil), + fKinematicCCT(true) +{ + ICreateController(fLocalPosition); + fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); gControllers.push_back(this); - fLastGlobalLoc.Reset(); - ICreateController(); - Enable(false); -} - -void plPXPhysicalControllerCore::ISetGlobalLoc(const hsMatrix44& l2w) -{ - fLastGlobalLoc = l2w; - // Update our subworld position and rotation - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) - { - const hsMatrix44& w2s = fPrevSubworldW2L; - hsMatrix44 l2s = w2s * l2w; - - l2s.GetTranslate(&fLocalPosition); - fLocalRotation.SetFromMatrix44(l2s); - } - else - { - l2w.GetTranslate(&fLocalPosition); - fLocalRotation.SetFromMatrix44(l2w); - } - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - if (fProxyGen) - fProxyGen->SetTransform(l2w, w2l); - // Update the physical position - NxExtendedVec3 nxPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kPhysZOffset); - fController->setPosition(nxPos); - IMatchKinematicToController(); } plPXPhysicalControllerCore::~plPXPhysicalControllerCore() { - IDeleteController(); - for (int i = 0; i < gControllers.size(); i++) + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { if (gControllers[i] == this) { @@ -347,445 +188,89 @@ plPXPhysicalControllerCore::~plPXPhysicalControllerCore() break; } } - delete fProxyGen; -} -void plPXPhysicalControllerCore::IMatchKinematicToController() -{ - if ( fKinematicActor) - { - NxExtendedVec3 cPos = fController->getPosition(); - NxVec3 prevKinPos = fKinematicActor->getGlobalPosition(); - NxVec3 kinPos; - kinPos.x = (NxReal)cPos.x; - kinPos.y = (NxReal)cPos.y; - kinPos.z = (NxReal)cPos.z; - if (plSimulationMgr::fExtraProfile) - SimLog("Match setting kinematic from %f,%f,%f to %f,%f,%f",prevKinPos.x,prevKinPos.y,prevKinPos.z,kinPos.x,kinPos.y,kinPos.z ); - if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - fKinematicActor->moveGlobalPosition(kinPos); - else - fKinematicActor->setGlobalPosition(kinPos); - } -} -void plPXPhysicalControllerCore::UpdateControllerAndPhysicalRep() -{ - if ( fKinematicActor) - { - if(this->fBehavingLikeAnimatedPhys) - {//this means we are moving the controller and then synchnig the kin - NxExtendedVec3 ControllerPos= fController->getPosition(); - NxVec3 NewKinPos((NxReal)ControllerPos.x, (NxReal)ControllerPos.y, (NxReal)ControllerPos.z); - if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - { - if (plSimulationMgr::fExtraProfile) - SimLog("Moving kinematic to %f,%f,%f",NewKinPos.x, NewKinPos.y, NewKinPos.z ); - // use the position - fKinematicActor->moveGlobalPosition(NewKinPos); - - } - else - { - if (plSimulationMgr::fExtraProfile) - SimLog("Setting kinematic to %f,%f,%f", NewKinPos.x, NewKinPos.y, NewKinPos.z ); - fKinematicActor->setGlobalPosition(NewKinPos); - } - - } - else - { - NxVec3 KinPos= fKinematicActor->getGlobalPosition(); - NxExtendedVec3 NewControllerPos(KinPos.x, KinPos.y, KinPos.z); - if (plSimulationMgr::fExtraProfile) - SimLog("Setting Controller to %f,%f,%f", NewControllerPos.x, NewControllerPos.y, NewControllerPos.z ); - fController->setPosition(NewControllerPos); - } - hsPoint3 curLocalPos; - GetPositionSim(curLocalPos); - fLocalPosition = curLocalPos; - } -} -void plPXPhysicalControllerCore::MoveKinematicToController(hsPoint3& pos) -{ - if ( fKinematicActor) - { - NxVec3 kinPos = fKinematicActor->getGlobalPosition(); - if ( abs(kinPos.x-pos.fX) + abs(kinPos.y-pos.fY) + (abs(kinPos.z-pos.fZ+kPhysZOffset)) > 0.0001f) - { - NxVec3 newPos; - newPos.x = (NxReal)pos.fX; - newPos.y = (NxReal)pos.fY; - newPos.z = (NxReal)pos.fZ+kPhysZOffset; - if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - { - if (plSimulationMgr::fExtraProfile) - SimLog("Moving kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); - // use the position - fKinematicActor->moveGlobalPosition(newPos); - } - else - { - if (plSimulationMgr::fExtraProfile) - SimLog("Setting kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); - fKinematicActor->setGlobalPosition(newPos); - } - } - } -} - -void plPXPhysicalControllerCore::ISetKinematicLoc(const hsMatrix44& l2w) -{ - hsPoint3 kPos; - // Update our subworld position and rotation - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) - { - const hsMatrix44& w2s = subworldCI->GetWorldToLocal(); - hsMatrix44 l2s = w2s * l2w; - - l2s.GetTranslate(&kPos); - } - else - { - l2w.GetTranslate(&kPos); - } - - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - if (fProxyGen) - fProxyGen->SetTransform(l2w, w2l); + IDeleteController(); - // add z offset - kPos.fZ += kPhysZOffset; - // Update the physical position of kinematic - if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - fKinematicActor->moveGlobalPosition(plPXConvert::Point(kPos)); - else - fKinematicActor->setGlobalPosition(plPXConvert::Point(kPos)); -} -void plPXPhysicalControllerCore::IGetPositionSim(hsPoint3& pos) const -{ - - if(this->fBehavingLikeAnimatedPhys) - { - const NxExtendedVec3& nxPos = fController->getPosition(); - pos.Set(float(nxPos.x), float(nxPos.y), float(nxPos.z) - kPhysZOffset); - } - else - { - NxVec3 Pos = fKinematicActor->getGlobalPosition(); - pos.Set(float(Pos.x), float(Pos.y), float(Pos.z) - kPhysZOffset); - } -} -void plPXPhysicalControllerCore::ICreateController() -{ -NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); - - NxCapsuleControllerDesc desc; - desc.position.x = 0; - desc.position.y = 0; - desc.position.z = -10000; // No one should be building down there... (-2000 is kickable limit) - desc.upDirection = NX_Z; - desc.slopeLimit = kSLOPELIMIT; - desc.skinWidth = kPhysxSkinWidth; - desc.stepOffset = STEP_OFFSET; - desc.callback = &gMyReport; - desc.userData = this; - desc.radius = fRadius; - desc.height = fHeight; - desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; - //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; - fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); - - // Change the avatars shape groups. The avatar doesn't actually use these when - // it's determining collision, but if you're standing still and an object runs - // into you, it'll pass through without this. - NxActor* actor = fController->getActor(); - NxShape* shape = actor->getShapes()[0]; - shape->setGroup(plSimDefs::kGroupAvatar); - - // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. - NxActorDesc actorDesc; - NxCapsuleShapeDesc capDesc; - capDesc.radius = fRadius; - capDesc.height = fHeight; - capDesc.group = plSimDefs::kGroupAvatar; - capDesc.materialIndex= plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0,0.0); - actorDesc.shapes.pushBack(&capDesc); - NxBodyDesc bodyDesc; - bodyDesc.mass = kAvatarMass; - actorDesc.body = &bodyDesc; - bodyDesc.flags = NX_BF_KINEMATIC; - bodyDesc.flags |=NX_BF_DISABLE_GRAVITY ; - - actorDesc.name = "AvatarTriggerKinematicGuy"; - fSeeking=false; - try + // Release any queued messages we may have + int numMsgs = fQueuedCollideMsgs.size(); + if (numMsgs) { - fKinematicActor = scene->createActor(actorDesc); - } catch (...) - { - hsAssert(false, "Actor creation crashed"); - } -#ifdef PHYSX_KINEMATIC_IS_DISABLED - // initially start as in-active - fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); -#endif - // set the matrix to be the same as the controller's actor... that should orient it to be the same - // PhysX HACK TURD: Sometimes, using the ***wrong*** function is desirable... - // Changing this to moveGlobalPose will cause camera regions to fire before linking in. - fKinematicActor->setGlobalPose(actor->getGlobalPose()); - - // the proxy for the debug display - //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); + for (int i = 0; i < numMsgs; ++i) + delete fQueuedCollideMsgs[i]; - hsColorRGBA physColor; - float opac = 1.0f; - - // local avatar is light purple and transparent - physColor.Set(.2f, .1f, .2f, 1.f); - opac = 0.8f; - - /* - // the avatar proxy doesn't seem to work... not sure why? - fProxyGen = new plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); - fProxyGen->Init(this); - */ -} -void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos) -{ - NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); - NxCapsuleControllerDesc desc; - desc.position.x = pos.fX; - desc.position.y = pos.fY; - desc.position.z = pos.fZ; - desc.upDirection = NX_Z; - desc.slopeLimit = kSLOPELIMIT; - desc.skinWidth = kPhysxSkinWidth; - desc.stepOffset = STEP_OFFSET; - desc.callback = &gMyReport; - desc.userData = this; - desc.radius = fRadius; - desc.height = fHeight; - desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; - //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; - fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); - - // Change the avatars shape groups. The avatar doesn't actually use these when - // it's determining collision, but if you're standing still and an object runs - // into you, it'll pass through without this. - NxActor* actor = fController->getActor(); - NxShape* shape = actor->getShapes()[0]; - shape->setGroup(plSimDefs::kGroupAvatar); - - // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. - NxActorDesc actorDesc; - NxCapsuleShapeDesc capDesc; - capDesc.radius = fRadius; - capDesc.height = fHeight; - capDesc.group = plSimDefs::kGroupAvatar; - actorDesc.shapes.pushBack(&capDesc); - capDesc.materialIndex= plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0,0.0); - actorDesc.globalPose=actor->getGlobalPose(); - NxBodyDesc bodyDesc; - bodyDesc.mass = kAvatarMass; - actorDesc.body = &bodyDesc; - bodyDesc.flags = NX_BF_KINEMATIC; - bodyDesc.flags |=NX_BF_DISABLE_GRAVITY ; - actorDesc.name = "AvatarTriggerKinematicGuy"; - fSeeking=false; - try - { - fKinematicActor = scene->createActor(actorDesc); - } - catch (...) - { - hsAssert(false, "Actor creation crashed"); + fQueuedCollideMsgs.clear(); } - // set the matrix to be the same as the controller's actor... that should orient it to be the same - //fKinematicActor->setGlobalPose(actor->getGlobalPose()); - - // the proxy for the debug display - //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); - - hsColorRGBA physColor; - float opac = 1.0f; - - // local avatar is light purple and transparent - physColor.Set(.2f, .1f, .2f, 1.f); - opac = 0.8f; - - /* - // the avatar proxy doesn't seem to work... not sure why? - fProxyGen = new plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); - fProxyGen->Init(this); - */ - -} -void plPXPhysicalControllerCore::IDeleteController() -{ - if (fController) - { - gControllerMgr.releaseController(*fController); - fController = nil; - - if (fKinematicActor) - { - NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); - scene->releaseActor(*fKinematicActor); - fKinematicActor = nil; - } - plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); - } + delete fProxyGen; } -void plPXPhysicalControllerCore::IInformDetectors(bool entering,bool deferUntilNextSim=true) -{ - static const NxU32 DetectorFlag= 1<GetScene(fWorldKey); - int kNumofShapesToStore=30; - NxCapsule cap; - GetWorldSpaceCapsule(cap); - NxShape* shapes[30]; - int numCollided=scene->overlapCapsuleShapes(cap,NX_ALL_SHAPES,kNumofShapesToStore,shapes,NULL,DetectorFlag,NULL,true); - for (int i=0;igetActor()); - - if (myactor) - { - plPXPhysical* physical = (plPXPhysical*)myactor->userData; - if (physical) - { - bool doReport = physical->DoReportOn(plSimDefs::kGroupAvatar); - if(doReport) - { - plCollideMsg* msg = new plCollideMsg; - msg->fOtherKey = fOwner; - msg->fEntering = entering; - msg->AddReceiver(physical->GetObjectKey()); - if(!deferUntilNextSim) - { - DetectorLog("Sending an %s msg to %s" , entering? "entering":"exit", physical->GetObjectKey()->GetName().c_str()); - msg->Send(); - } - else - { - DetectorLog("Queuing an %s msg to %s, which will be sent after the client update" , entering? "entering":"exit", physical->GetObjectKey()->GetName().c_str()); - plgDispatch::Dispatch()->MsgQueue(msg); - } - } - } - } - } - DetectorLog("Done informing from plPXPhysicalControllerCore::IInformDetectors"); - } -} -void plPXPhysicalControllerCore::Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) -{ - collisionResults=0; - if(fController) - { - NxVec3 dis(displacement.fX,displacement.fY,displacement.fZ); - NxU32 colFlags = 0; - this->fController->move(dis,collideWith,.00001,colFlags); - if(colFlags&NXCC_COLLISION_DOWN)collisionResults|=kBottom; - if(colFlags&NXCC_COLLISION_UP)collisionResults|=kTop; - if(colFlags&&NXCC_COLLISION_SIDES)collisionResults|=kSides; - } - return; -} void plPXPhysicalControllerCore::Enable(bool enable) { if (fEnabled != enable) { fEnabled = enable; if (fEnabled) + { + // Defer until the next physics update. fEnableChanged = true; + } else { - // See ISendUpdates for why we don't re-enable right away - fController->setCollision(fEnabled); + 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) { - bool wasEnabled = fEnabled; -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - // PHYSX FIXME - before leaving this world, sending leaving detector events if we are inside a convex hull detector - hsPoint3 pos; - IGetPositionSim(pos); - plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,false); -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - //need to inform detectors in the old world that we are leaving - IInformDetectors(false); - //done informing old world SimLog("Changing subworlds!"); + + // Inform detectors in the old world that we are leaving + IInformDetectors(false); IDeleteController(); - SimLog("Deleted old controller"); + + // Update Local Position and rotation fWorldKey = world; - if (GetSubworldCI()) - fPrevSubworldW2L = GetSubworldCI()->GetWorldToLocal(); - // Update our subworld position and rotation const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { - const hsMatrix44& w2s = fPrevSubworldW2L; - hsMatrix44 l2s = w2s * fLastGlobalLoc; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + hsMatrix44 l2s = fPrevSubworldW2L * fLastGlobalLoc; l2s.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(l2s); } else { + fPrevSubworldW2L.Reset(); fLastGlobalLoc.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(fLastGlobalLoc); } - hsMatrix44 w2l; - fLastGlobalLoc.GetInverse(&w2l); - if (fProxyGen) - fProxyGen->SetTransform(fLastGlobalLoc, w2l); - // Update the physical position - SimLog("creating new controller"); - hsPoint3 PositionPlusOffset=fLocalPosition; - PositionPlusOffset.fZ +=kPhysZOffset; - //placing new controller and kinematic in the appropriate location - ICreateController(PositionPlusOffset); - RebuildCache(); - } -} -const plCoordinateInterface* plPXPhysicalControllerCore::GetSubworldCI() const -{ - if (fWorldKey) - { - plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); - if (so) - return so->GetCoordinateInterface(); + + fLastLocalPosition = fLocalPosition; + + // Create new controller + ICreateController(fLocalPosition); } - return nil; } -// For the avatar SDL only + 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 * M_PI) - zRot; // axis is backwards, so reverse the angle too + 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()); @@ -811,361 +296,525 @@ void plPXPhysicalControllerCore::SetState(const hsPoint3& pos, float zRot) so->FlushTransform(); } } -// kinematic stuff .... should be just for when playing a behavior... -void plPXPhysicalControllerCore::Kinematic(bool state) + +void plPXPhysicalControllerCore::SetMovementStrategy(plMovementStrategy* strategy) { - if (fKinematic != state) + if (fKinematicCCT != strategy->IsKinematic()) { - fKinematic = state; - if (fKinematic) - { - // See ISendUpdates for why we don't re-enable right away - fController->setCollision(false); -#ifdef PHYSX_KINEMATIC_IS_DISABLED - fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); -#endif - } - else - { - fKinematicChanged = true; - } + IDeleteController(); + fKinematicCCT = !fKinematicCCT; + ICreateController(fLocalPosition); } + + fMovementStrategy = strategy; } -bool plPXPhysicalControllerCore::IsKinematic() -{ - return fKinematic; -} -void plPXPhysicalControllerCore::GetKinematicPosition(hsPoint3& pos) + +void plPXPhysicalControllerCore::SetGlobalLoc(const hsMatrix44& l2w) { - pos.Set(-1,-1,-1); - if ( fKinematicActor ) + fLastGlobalLoc = l2w; + + // Update our local position and rotation + 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) { - NxVec3 klPos = fKinematicActor->getGlobalPosition(); - pos.Set(float(klPos.x), float(klPos.y), float(klPos.z) - kPhysZOffset); + NxExtendedVec3 pos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kCCTZOffset); + fController->setPosition(pos); + } + 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::UpdatePoststep( float delSecs) + +void plPXPhysicalControllerCore::GetPositionSim(hsPoint3& pos) { - // Apparently the user data field of the controllers is broken -// uint32_t count = gControllerMgr.getNbControllers(); -// NxController* controllers = (NxController*)gControllerMgr.getControllers(); -// -// for (int i = 0; i < count; i++) -// { -// plPXPhysicalController* ac = (plPXPhysicalController*)controllers[i].getAppData(); - for (int i = 0; i < gControllers.size(); i++) + if (fKinematicCCT) { - plPXPhysicalControllerCore* ac = gControllers[i]; - - hsAssert(ac, "Bad avatar controller"); - - gControllerMgr.updateControllers(); - if(ac->fMovementInterface) - ac->Update(delSecs); - - if (ac->GetSubworldCI()) - ac->fPrevSubworldW2L = ac->GetSubworldCI()->GetWorldToLocal(); - else - { - if (!ac->fPrevSubworldW2L.IsIdentity()) - ac->fPrevSubworldW2L.Reset(); - } + 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::UpdatePrestep(float delSecs) + +void plPXPhysicalControllerCore::Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) { - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalControllerCore* ac = gControllers[i]; + NxU32 colFlags = 0; - hsAssert(ac, "Bad avatar controller"); - //FIXME -#ifndef PLASMA_EXTERNAL_RELEASE - ac->fDbgCollisionInfo.SetCount(0); -#endif // PLASMA_EXTERNAL_RELEASE + fController->move(plPXConvert::Vector(displacement), collideWith, 0.00001f, colFlags); - if (gRebuildCache&&ac->fController) - ac->fController->reportSceneChanged(); - //hsAssert(ac->fMovementInterface,"Updating a controller with out a movement strategy"); - if(ac) - { - if(ac->fNeedsResize)ac->IHandleResize(); - ac->Apply(delSecs); - } -#ifndef PLASMA_EXTERNAL_RELEASE - if (fDebugDisplay) - ac->IDrawDebugDisplay(); -#endif // PLASMA_EXTERNAL_RELEASE - } - gRebuildCache = false; + 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::UpdatePostSimStep(float delSecs) + +void plPXPhysicalControllerCore::SetLinearVelocitySim(const hsVector3& linearVel) { - for (int i = 0; i < gControllers.size(); i++) + if (!fKinematicCCT) { - plPXPhysicalControllerCore* ac = gControllers[i]; - hsAssert(ac, "Bad avatar controller"); - ac->PostStep(delSecs); - + NxVec3 vel = plPXConvert::Vector(linearVel); + fActor->setLinearVelocity(vel); } } -void plPXPhysicalControllerCore::HandleEnableChanged() + +int plPXPhysicalControllerCore::SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, bool vsStatics, + uint32_t& vsSimGroups, std::vector& hits) { - fEnableChanged = false; - if(this->fBehavingLikeAnimatedPhys) - { - fController->setCollision(fEnabled); - } - else + 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) { - fController->setCollision(false); + 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); + } } -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - // PHYSX FIXME - after re-enabling check to see if we are inside any convex hull detector regions - hsPoint3 pos; - IGetPositionSim(pos); - plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,fEnabled); -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - //IInformDetectors(true); + } + + return hits.size(); } -void plPXPhysicalControllerCore::HandleKinematicChanged() +void plPXPhysicalControllerCore::LeaveAge() { - fKinematicChanged = false; - if(this->fBehavingLikeAnimatedPhys) - { - fController->setCollision(true); - } - else - { - fController->setCollision(false); - } -#ifdef PHYSX_KINEMATIC_IS_DISABLED - fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); -#endif // PHYSX_KINEMATIC_IS_DISABLED + SetPushingPhysical(nil); + if (fWorldKey) + SetSubworld(nil); + + // Disable all collisions + fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); } -void plPXPhysicalControllerCore::HandleKinematicEnableNextUpdate() + +void plPXPhysicalControllerCore::GetWorldSpaceCapsule(NxCapsule& cap) const { - fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); - fKinematicEnableNextUpdate = false; + NxShape* shape = fActor->getShapes()[0]; + NxCapsuleShape* capShape = shape->isCapsule(); + capShape->getWorldCapsule(cap); } -void plPXPhysicalControllerCore::IHandleResize() + +plDrawableSpans* plPXPhysicalControllerCore::CreateProxy(hsGMaterial* mat, hsTArray& 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); - uint32_t collideFlags = - 1<ObjectIsLoaded()); + if (so) { - collideFlags|=(1<GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + + myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, + mat, so->GetLocalToWorld(), blended, + nil, &idx, myDraw); } - NxScene* myscene = plSimulationMgr::GetInstance()->GetScene(this->fWorldKey); -// NxShape** response=new NxShape*[2]; - - NxVec3 center(fLocalPosition.fX,fLocalPosition.fY,fLocalPosition.fZ+fPreferedRadius); - NxSegment Seg(center,center); - const NxCapsule newCap(Seg,fPreferedRadius); - int numintersect =myscene->checkOverlapCapsule(newCap,NX_ALL_SHAPES,collideFlags); - //with new capsule dimensions check for overlap - //with objects we would collide with - - if(numintersect==0) +*/ + return myDraw; +} + +void plPXPhysicalControllerCore::AddDynamicHit(plPXPhysical* phys) +{ + int numHits = fDynamicHits.size(); + for (int i = 0; i < numHits; ++i) { - fHeight=fPreferedHeight; - fRadius=fPreferedRadius; - fController->setRadius(fRadius); - fController->setHeight(fHeight); - - fNeedsResize=false; + if (fDynamicHits[i] == phys) + return; } -// delete[] response; + fDynamicHits.push_back(phys); } -void plPXPhysicalControllerCore::SetControllerDimensions(float radius, float height) + +void plPXPhysicalControllerCore::Apply(float delSecs) { - fNeedsResize=false; - if(fRadius!=radius) + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { - fNeedsResize=true; + 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(); } - if(fHeight!=height) + + gRebuildCache = false; +} +void plPXPhysicalControllerCore::Update(int numSubSteps, float alpha) +{ + gControllerMgr.updateControllers(); + + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { - fNeedsResize=true; + controller = gControllers[i]; + + controller->IUpdate(numSubSteps, alpha); + +#ifndef PLASMA_EXTERNAL_RELEASE + if (fDebugDisplay) + controller->IDrawDebugDisplay(); +#endif } - fPreferedRadius=radius; - fPreferedHeight=height; } - -void plPXPhysicalControllerCore::LeaveAge() +void plPXPhysicalControllerCore::UpdateNonPhysical(float alpha) { - SetPushingPhysical(nil); - if(fWorldKey) this->SetSubworld(nil); - this->fMovementInterface->LeaveAge(); + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) + { + controller = gControllers[i]; + controller->IUpdateNonPhysical(alpha); + } } -int plPXPhysicalControllerCore::SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, bool vsStatics, - uint32_t& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut) + +void plPXPhysicalControllerCore::RebuildCache() { gRebuildCache = true; } + +plPXPhysicalControllerCore* plPXPhysicalControllerCore::GetController(NxActor& actor) { - NxCapsule tempCap; - tempCap.p0 =plPXConvert::Point( startPos); - tempCap.p0.z = tempCap.p0.z + fPreferedRadius; - tempCap.radius = fPreferedRadius ; - tempCap.p1 = tempCap.p0; - tempCap.p1.z = tempCap.p1.z + fPreferedHeight; + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) + { + controller = gControllers[i]; + if (controller->fActor == &actor) + return controller; + } - NxVec3 vec; - vec.x = endPos.fX - startPos.fX; - vec.y = endPos.fY - startPos.fY; - vec.z = endPos.fZ - startPos.fZ; + return nil; +} - int numberofHits = 0; - int HitsReturned = 0; - WhatWasHitOut.clear(); - NxScene *myscene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); - NxSweepQueryHit whatdidIhit[10]; - unsigned int flags = NX_SF_ALL_HITS; - if(vsDynamics) - flags |= NX_SF_DYNAMICS; - if(vsStatics) - flags |= NX_SF_STATICS; - numberofHits = myscene->linearCapsuleSweep(tempCap, vec, flags, nil, 10, whatdidIhit, nil, vsSimGroups); - if(numberofHits) - {//we hit a dynamic object lets make sure it is not animatable - for(int i=0; igetActor().userData; - CurrentHit.Norm.fX = whatdidIhit[i].normal.x; - CurrentHit.Norm.fY = whatdidIhit[i].normal.y; - CurrentHit.Norm.fZ = whatdidIhit[i].normal.z; - if(CurrentHit.ObjHit != nil) - { - hsPoint3 where; - where.fX = whatdidIhit[i].point.x; - where.fY = whatdidIhit[i].point.y; - where.fZ = whatdidIhit[i].point.z; - CurrentHit.locHit = where; - CurrentHit.TimeHit = whatdidIhit[i].t ; - WhatWasHitOut.insert(CurrentHit); - HitsReturned++; - } - } +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 HitsReturned; + return count; } -void plPXPhysicalControllerCore::BehaveLikeAnimatedPhysical(bool actLikeAnAnimatedPhys) +int plPXPhysicalControllerCore::GetControllersInThisSubWorld(plKey world, int maxToReturn, plPXPhysicalControllerCore** bufferout) { - hsAssert(fKinematicActor, "Changing behavior, but plPXPhysicalControllerCore has no Kinematic actor associated with it"); - if(fBehavingLikeAnimatedPhys!=actLikeAnAnimatedPhys) + int count = 0; + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { - fBehavingLikeAnimatedPhys=actLikeAnAnimatedPhys; - if(fKinematicActor) + controller = gControllers[i]; + if (controller->GetSubworld() == world) { - if(actLikeAnAnimatedPhys) + if (count < maxToReturn) { - //need to set BX Kinematic if true and kill any rotation - fController->setCollision(fEnabled); - fKinematicActor->raiseBodyFlag(NX_BF_KINEMATIC); - fKinematicActor->clearBodyFlag(NX_BF_FROZEN_ROT); - fKinematicActor->raiseBodyFlag(NX_BF_DISABLE_GRAVITY); - } - else - { - //don't really use the controller now don't bother with collisions - fController->setCollision(false); - fKinematicActor->clearBodyFlag(NX_BF_KINEMATIC); - fKinematicActor->raiseBodyFlag(NX_BF_FROZEN_ROT_X); - fKinematicActor->raiseBodyFlag(NX_BF_FROZEN_ROT_Y); - fKinematicActor->clearBodyFlag(NX_BF_DISABLE_GRAVITY); - - + bufferout[count] = controller; + ++count; } } } -} -bool plPXPhysicalControllerCore::BehavingLikeAnAnimatedPhysical() -{ - hsAssert(fKinematicActor, "plPXPhysicalControllerCore is missing a kinematic actor"); - return fBehavingLikeAnimatedPhys; + return count; } -void plPXPhysicalControllerCore::SetLinearVelocity(const hsVector3& linearVel) +int plPXPhysicalControllerCore::NumControllers() { return gControllers.size(); } + +void plPXPhysicalControllerCore::IHandleEnableChanged() { - plPhysicalControllerCore::SetLinearVelocity(linearVel); - if(fKinematicActor && !fBehavingLikeAnimatedPhys) + // Defered enable + fEnableChanged = false; + + if (!fKinematicCCT) { - NxVec3 vel= plPXConvert::Vector(linearVel); - fKinematicActor->setLinearVelocity(vel); + // 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::SetAngularVelocity(const float angvel) + +void plPXPhysicalControllerCore::IInformDetectors(bool entering) { - plPhysicalControllerCore::SetAngularVelocity(angvel); - if(fKinematicActor && !fBehavingLikeAnimatedPhys) + static const NxU32 kDetectorFlag = 1<GetScene(fWorldKey); + int numCollided = scene->overlapCapsuleShapes(capsule, NX_ALL_SHAPES, kNumShapes, shapes, NULL, kDetectorFlag, NULL, true); + for (int i = 0; i < numCollided; ++i) { - NxVec3 vel(0.0f, 0.0f, angvel); - fKinematicActor->setAngularVelocity(vel); + 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); + } } -} -void plPXPhysicalControllerCore::SetVelocities(const hsVector3& linearVel, float angVel) -{ - SetLinearVelocity(linearVel); - SetAngularVelocity(angVel); + + DetectorLog("Done informing from plPXPhysicalControllerCore::IInformDetectors"); } -void plPXPhysicalControllerCore::IMatchControllerToKinematic() +void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos) { - NxExtendedVec3 newpos; - NxVec3 pos=fKinematicActor->getGlobalPosition(); - newpos.x=pos.x; - newpos.y=pos.y; - newpos.z=pos.z; - fController->setPosition(newpos); + 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 + kPhysHeightCorrection; + NxCapsuleShape* capShape = shape->isCapsule(); + capShape->setDimensions(kineRadius, kineHeight); + capShape->setLocalPosition(NxVec3(0.0f, (kPhysHeightCorrection / 2.0f), 0.0f)); + } + 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); + */ } -const hsVector3& plPXPhysicalControllerCore::GetLinearVelocity() +void plPXPhysicalControllerCore::IDeleteController() { - if(BehavingLikeAnAnimatedPhysical()) - return fLinearVelocity; + if (fKinematicCCT) + { + gControllerMgr.releaseController(*fController); + fController = nil; + } else { - fLinearVelocity = plPXConvert::Vector(fKinematicActor->getLinearVelocity()); - return fLinearVelocity; + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + scene->releaseActor(*fActor); } + + fActor = nil; + plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); } -// Make a visible object that can be viewed by users for debugging purposes. -plDrawableSpans* plPXPhysicalControllerCore::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +void plPXPhysicalControllerCore::IDispatchQueuedMsgs() { - 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); + int numMsgs = fQueuedCollideMsgs.size(); + if (numMsgs) + { + plSimulationMgr* simMgr = plSimulationMgr::GetInstance(); + for (int i = 0; i < numMsgs; ++i) + simMgr->AddCollisionMsg(fQueuedCollideMsgs[i]); -/* - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) + fQueuedCollideMsgs.clear(); + } +} +void plPXPhysicalControllerCore::IProcessDynamicHits() +{ + int numHits = fDynamicHits.size(); + if (numHits) { - bool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + plPXPhysical* phys; + plSimulationMgr* simMgr = plSimulationMgr::GetInstance(); + for (int i = 0; i < numHits; ++i) + { + phys = fDynamicHits[i]; - myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, - mat, so->GetLocalToWorld(), blended, - nil, &idx, myDraw); + // 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(); } -*/ - return myDraw; } + #ifndef PLASMA_EXTERNAL_RELEASE +#include "../plPipeline/plDebugText.h" void plPXPhysicalControllerCore::IDrawDebugDisplay() { plDebugText &debugTxt = plDebugText::Instance(); char strBuf[ 2048 ]; int lineHeight = debugTxt.GetFontSize() + 4; - uint32_t scrnWidth, scrnHeight; + uint32_t scrnWidth, scrnHeight; debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); int y = 10; @@ -1185,10 +834,11 @@ void plPXPhysicalControllerCore::IDrawDebugDisplay() char *overlapStr = fDbgCollisionInfo[i].fOverlap ? "yes" : "no"; float angle = hsRadiansToDegrees(acos(normal * hsVector3(0, 0, 1))); sprintf(strBuf, " Obj: %s, Normal: (%.2f, %.2f, %.2f), Angle(%.1f), Overlap(%3s)", - fDbgCollisionInfo[i].fSO->GetKeyName().c_str(), + fDbgCollisionInfo[i].fSO->GetKeyName(), normal.fX, normal.fY, normal.fZ, angle, overlapStr); debugTxt.DrawString(x, y, strBuf); y += lineHeight; } } #endif PLASMA_EXTERNAL_RELEASE + diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h index a2dad48e..4e80b6ff 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h @@ -40,22 +40,19 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plAvatar/plPhysicalControllerCore.h" -#include "hsQuat.h" -#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 -class NxController; class NxCapsuleController; class NxActor; -class plCoordinateInterface; +class NxCapsule; +class PXControllerHitReport; class plPhysicalProxy; class plDrawableSpans; class hsGMaterial; -class NxCapsule; class plSceneObject; -class PXControllerHitReportWalk; +class plPXPhysical; class plCollideMsg; -#ifndef PLASMA_EXTERNAL_RELEASE +#ifndef PLASMA_EXTERNAL_RELEASE class plDbgCollisionInfo { public: @@ -64,97 +61,112 @@ public: bool fOverlap; }; #endif // PLASMA_EXTERNAL_RELEASE + class plPXPhysicalControllerCore: public plPhysicalControllerCore { - friend class PXControllerHitReportWalk; public: plPXPhysicalControllerCore(plKey ownerSO, float height, float radius); ~plPXPhysicalControllerCore(); - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - - inline virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults); - // A disabled avatar doesn't move or accumulate air time if he's off the ground. + + // An ArmatureMod has its own idea about when physics should be enabled/disabled. + // Use plArmatureModBase::EnablePhysics() instead. virtual void Enable(bool enable); - - virtual void SetSubworld(plKey world) ; - virtual const plCoordinateInterface* GetSubworldCI() const ; + + // Subworld + virtual void SetSubworld(plKey world); + // For the avatar SDL only virtual void GetState(hsPoint3& pos, float& zRot); virtual void SetState(const hsPoint3& pos, float zRot); - // kinematic stuff .... should be just for when playing a behavior... - virtual void Kinematic(bool state); - virtual bool IsKinematic(); - virtual void GetKinematicPosition(hsPoint3& pos); - virtual const hsMatrix44& GetPrevSubworldW2L(){ return fPrevSubworldW2L; } - //when seeking no longer want to interact with exclusion regions - virtual void GetWorldSpaceCapsule(NxCapsule& cap) const; - static void RebuildCache(); - virtual const hsMatrix44& GetLastGlobalLoc(){return fLastGlobalLoc;} - virtual void SetKinematicLoc(const hsMatrix44& l2w){ISetKinematicLoc(l2w);} - virtual void SetGlobalLoc(const hsMatrix44& l2w){ISetGlobalLoc(l2w);} - virtual void HandleEnableChanged(); - virtual void HandleKinematicChanged(); - virtual void HandleKinematicEnableNextUpdate(); - virtual void GetPositionSim(hsPoint3& pos){IGetPositionSim(pos);} - virtual void MoveKinematicToController(hsPoint3& pos); - virtual const hsPoint3& GetLocalPosition(){return fLocalPosition;} - virtual void SetControllerDimensions(float radius, float height); + + // Movement strategy + virtual void SetMovementStrategy(plMovementStrategy* strategy); + + // Global location + virtual void SetGlobalLoc(const hsMatrix44& l2w); + + // Local Sim Position + virtual void GetPositionSim(hsPoint3& pos); + + // Move kinematic controller + virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults); + + // Set linear velocity on dynamic controller + virtual void SetLinearVelocitySim(const hsVector3& linearVel); + + // Sweep the controller path from startPos through endPos + virtual int SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, + bool vsStatics, uint32_t& vsSimGroups, std::vector& hits); + + // any clean up for the controller should go here virtual void LeaveAge(); - virtual void UpdateControllerAndPhysicalRep(); + + // Capsule + void GetWorldSpaceCapsule(NxCapsule& cap) const; + + // Create Proxy for debug rendering + plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + + // Dynamic hits + void AddDynamicHit(plPXPhysical* phys); ////////////////////////////////////////// //Static Helper Functions //////////////////////////////////////// - // Used by the LOS mgr to find the controller for an actor it hit - static plPXPhysicalControllerCore* GetController(NxActor& actor, bool* isController); - // test to see if there are any controllers (i.e. avatars) in this subworld + + // Call pre-sim to apply movement to controllers + static void Apply(float delSecs); + + // Call post-sim to update controllers + static void Update(int numSubSteps, float alpha); + + // Update controllers when not performing a physics step + static void UpdateNonPhysical(float alpha); + + // Rebuild the controller cache, required when a static actor in the scene has changed. + static void RebuildCache(); + + // Returns the plPXPhysicalControllerCore associated with the given NxActor + static plPXPhysicalControllerCore* GetController(NxActor& actor); + + // Subworld controller queries static bool AnyControllersInThisWorld(plKey world); - static int NumControllers(); - static int GetControllersInThisSubWorld(plKey world, int maxToReturn, - plPXPhysicalControllerCore** bufferout); static int GetNumberOfControllersInThisSubWorld(plKey world); - static void UpdatePrestep(float delSecs); - static void UpdatePoststep(float delSecs); - static void UpdatePostSimStep(float delSecs); - virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); -#ifndef PLASMA_EXTERNAL_RELEASE - static bool fDebugDisplay; -#endif // PLASMA_EXTERNAL_RELEASE + static int GetControllersInThisSubWorld(plKey world, int maxToReturn, plPXPhysicalControllerCore** bufferout); + + // Controller count + static int NumControllers(); static void SetMaxNumberOfControllers(int max) { fPXControllersMax = max; } static int fPXControllersMax; - virtual int SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, bool vsStatics, uint32_t& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut); - virtual void BehaveLikeAnimatedPhysical(bool actLikeAnAnimatedPhys); - virtual bool BehavingLikeAnAnimatedPhysical(); - virtual const hsVector3& GetLinearVelocity(); - virtual void SetLinearVelocity(const hsVector3& linearVel); - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void SetAngularVelocity(const float angvel); - virtual void SetVelocities(const hsVector3& linearVel, float angVel); +#ifndef PLASMA_EXTERNAL_RELEASE + static bool fDebugDisplay; +#endif protected: friend class PXControllerHitReport; - static plPXPhysicalControllerCore* FindController(NxController* controller); - void ISetGlobalLoc(const hsMatrix44& l2w); - void IMatchKinematicToController(); - void IMatchControllerToKinematic(); - void ISetKinematicLoc(const hsMatrix44& l2w); - void IGetPositionSim(hsPoint3& pos) const; - void ICreateController(); - void IDeleteController(); - void IInformDetectors(bool entering,bool deferUntilNextSim); + + virtual void IHandleEnableChanged(); + + void IInformDetectors(bool entering); + void ICreateController(const hsPoint3& pos); - NxActor* fKinematicActor; - NxCapsuleController* fController; + void IDeleteController(); + + void IDispatchQueuedMsgs(); + void IProcessDynamicHits(); + #ifndef PLASMA_EXTERNAL_RELEASE - hsTArray fDbgCollisionInfo; void IDrawDebugDisplay(); + hsTArray fDbgCollisionInfo; #endif - void IHandleResize(); - float fPreferedRadius; - float fPreferedHeight; - // The global position and rotation of the avatar last time we set it (so we - // can detect if someone else moves him) - plPhysicalProxy* fProxyGen; - bool fBehavingLikeAnimatedPhys; + + std::vector fQueuedCollideMsgs; + std::vector fDynamicHits; + + NxCapsuleController* fController; + NxActor* fActor; + + plPhysicalProxy* fProxyGen; + bool fKinematicCCT; }; diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index 5a4b768e..c7156c4f 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -70,80 +70,26 @@ 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; - bool 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()); + bool doReport = false; - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->%s %s (status=%x) other@(%f,%f,%f)",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit",status,otherPos.fX,otherPos.fY,otherPos.fZ); + // Get our trigger physical. This should definitely have a plPXPhysical + plPXPhysical* triggerPhys = (plPXPhysical*)triggerShape.getActor().userData; + // Get the triggerer. If it doesn't have a plPXPhyscial, it's an avatar 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().c_str(),triggerPhys->GetGroup(),otherPhys->GetObjectKey()->GetName().c_str(),otherPhys->GetGroup()); - } } else { - bool isController; - plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(otherShape.getActor(),&isController); + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(otherShape.getActor()); if (controller) { - if (isController) - { -#ifdef PHYSX_ONLY_TRIGGER_FROM_KINEMATIC - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s : ignoring controller events.",triggerPhys->GetObjectKey()->GetName().c_str()); - 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().c_str()); - 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().c_str()); - 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().c_str(),triggerPhys->GetGroup()); - } - else - { - hsPoint3 avpos; - controller->GetPositionSim(avpos); - DetectorLogRed("-->Avatar at (%f,%f,%f)",avpos.fX,avpos.fY,avpos.fZ); - } - } } } @@ -289,8 +235,8 @@ class ErrorStream : public NxUserOutputStream // ///////////////////////////////////////////////////////////////// -#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 +#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 ///////////////////////////////////////////////////////////////// // @@ -378,6 +324,7 @@ plSimulationMgr* plSimulationMgr::GetInstance() plSimulationMgr::plSimulationMgr() : fSuspended(true) + , fAccumulator(0.0f) , fLOSDispatch(new plLOSDispatch()) , fSoundMgr(new plPhysicsSoundMgr) , fLog(nil) @@ -470,6 +417,7 @@ NxScene* plSimulationMgr::GetScene(plKey world) scene->setGroupCollisionFlag(i, plSimDefs::kGroupDynamicBlocker, false); scene->setGroupCollisionFlag(i, plSimDefs::kGroupLOSOnly, false); scene->setGroupCollisionFlag(plSimDefs::kGroupLOSOnly, i, false); + scene->setGroupCollisionFlag(i, plSimDefs::kGroupAvatarKinematic, false); } scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatar, false); scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatarBlocker, true); @@ -477,7 +425,11 @@ NxScene* plSimulationMgr::GetScene(plKey world) scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupStatic, true); scene->setGroupCollisionFlag( plSimDefs::kGroupStatic, plSimDefs::kGroupAvatar, true); scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupDynamic, true); - + + // Kinematically controlled avatars interact with detectors and dynamics + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatarKinematic, plSimDefs::kGroupDetector, true); + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatarKinematic, 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); @@ -564,8 +516,7 @@ void plSimulationMgr::UpdateAvatarInDetector(plKey world, plPXPhysical* detector if ( actors[i]->userData == nil ) { // we go a controller - bool isController; - plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(*actors[i],&isController); + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(*actors[i]); if (controller && controller->IsEnabled()) { plKey avatar = controller->GetOwner(); @@ -614,20 +565,39 @@ void plSimulationMgr::AddCollisionMsg(plKey hitee, plKey hitter, bool enter) pMsg->fEntering = enter; fCollideMsgs.push_back(pMsg); } +void plSimulationMgr::AddCollisionMsg(plCollideMsg* msg) +{ + fCollideMsgs.push_back(msg); +} void plSimulationMgr::Advance(float delSecs) { if (fSuspended) return; - plProfile_IncCount(StepLen, (int)(delSecs*1000)); + fAccumulator += delSecs; + if (fAccumulator < kDefaultStepSize) + { + // Not enough time has passed to perform a substep. + plPXPhysicalControllerCore::UpdateNonPhysical(fAccumulator / kDefaultStepSize); + return; + } + else if (fAccumulator > kDefaultMaxDelta) + { + if (fExtraProfile) + Log("Step clamped from %f to limit of %f", fAccumulator, kDefaultMaxDelta); + fAccumulator = kDefaultMaxDelta; + } -#ifndef PLASMA_EXTERNAL_RELASE - uint32_t stepTime = hsTimer::GetPrecTickCount(); -#endif + // Perform as many whole substeps as possible saving the remainder in our accumulator. + int numSubSteps = (int)(fAccumulator / kDefaultStepSize); + float delta = numSubSteps * kDefaultStepSize; + fAccumulator -= delta; + + plProfile_IncCount(StepLen, (int)(delta*1000)); plProfile_BeginTiming(Step); - plPXPhysicalControllerCore::UpdatePrestep(delSecs); - plPXPhysicalControllerCore::UpdatePoststep( delSecs); + + plPXPhysicalControllerCore::Apply(delta); for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++) { @@ -642,12 +612,13 @@ void plSimulationMgr::Advance(float delSecs) } if (do_advance) { - scene->simulate(delSecs); + scene->simulate(delta); scene->flushStream(); scene->fetchResults(NX_RIGID_BODY_FINISHED, true); } } - plPXPhysicalControllerCore::UpdatePostSimStep(delSecs); + + plPXPhysicalControllerCore::Update(numSubSteps, fAccumulator / kDefaultStepSize); plProfile_EndTiming(Step); #ifndef PLASMA_EXTERNAL_RELEASE @@ -730,9 +701,6 @@ void plSimulationMgr::ISendUpdates() plPXPhysical* physical = (plPXPhysical*)actors[i]->userData; if (physical) { - // apply any hit forces - physical->ApplyHitForce(); - if (physical->GetSceneNode()) { physical->SendNewLocation(); diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h index 2cf07d90..552ccbff 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h @@ -105,6 +105,7 @@ public: //Fix to Move collision messages and their handling out of the simulation step void AddCollisionMsg(plKey hitee, plKey hitter, bool entering); + void AddCollisionMsg(plCollideMsg* msg); #ifndef PLASMA_EXTERNAL_RELEASE static bool fDisplayAwakeActors; @@ -144,6 +145,8 @@ protected: // but nothing will move. bool fSuspended; + float fAccumulator; + // A utility class to keep track of a request for a physical synchronization. // These requests must pass a certain criteria (see the code for the latest) // before they are actually either sent over the network or rejected. diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h b/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h index 81da4934..c22edd7f 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h +++ b/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h @@ -68,8 +68,10 @@ namespace plSimDefs kGroupLOSOnly, //kExcludeRegion setting up so only blocks avatars and only when not in seek mode kGroupExcludeRegion, + // A kinematic avatar only interacts with dynamics and detectors + kGroupAvatarKinematic, // Just for error checking - kGroupMax, + kGroupMax }; /** Different types of line-of-sight requests. */