From 769cd6061715aa5634c2e084768e70674efb4e0f Mon Sep 17 00:00:00 2001 From: Skoader Date: Sun, 24 Jun 2012 12:51:45 +1000 Subject: [PATCH 01/15] Refactor avatar movement code plMovementStrategy classes have been reworked and completely replace all plAvatarControllers. While based on the old implementation, plPhysicalControllerCore has essentially been rewritten. Remnants of long gone physical "actions" have been removed. 4 files removed - plAVCallbackAction.h & plAVCallbackAction.cpp plAntiGravAction.h & plAntiGravAction.cpp This revision will not compile, requires new plPXPhysicalControllerCore implementation. --- .../Plasma/FeatureLib/pfAudio/plListener.cpp | 2 +- .../FeatureLib/pfCamera/plCameraModifier.cpp | 2 +- .../FeatureLib/pfPython/pySceneObject.cpp | 2 +- .../Plasma/PubUtilLib/plAvatar/CMakeLists.txt | 4 - .../PubUtilLib/plAvatar/plAnimStage.cpp | 1 - .../PubUtilLib/plAvatar/plAntiGravAction.cpp | 171 -- .../PubUtilLib/plAvatar/plAntiGravAction.h | 79 - .../PubUtilLib/plAvatar/plArmatureEffects.cpp | 1 - .../PubUtilLib/plAvatar/plArmatureMod.cpp | 17 +- .../PubUtilLib/plAvatar/plArmatureMod.h | 1 + .../PubUtilLib/plAvatar/plAvBrainCritter.cpp | 20 +- .../PubUtilLib/plAvatar/plAvBrainCritter.h | 6 +- .../PubUtilLib/plAvatar/plAvBrainDrive.cpp | 1 - .../PubUtilLib/plAvatar/plAvBrainGeneric.cpp | 4 - .../PubUtilLib/plAvatar/plAvBrainGeneric.h | 2 - .../PubUtilLib/plAvatar/plAvBrainHuman.cpp | 157 +- .../PubUtilLib/plAvatar/plAvBrainHuman.h | 4 +- .../plAvBrainRideAnimatedPhysical.cpp | 13 +- .../plAvatar/plAvBrainRideAnimatedPhysical.h | 2 - .../PubUtilLib/plAvatar/plAvBrainSwim.cpp | 137 +- .../PubUtilLib/plAvatar/plAvBrainSwim.h | 6 +- .../plAvatar/plAvCallbackAction.cpp | 579 ------- .../PubUtilLib/plAvatar/plAvCallbackAction.h | 217 --- .../plAvatar/plAvLadderModifier.cpp | 4 +- .../PubUtilLib/plAvatar/plAvTaskSeek.cpp | 6 +- .../plAvatar/plAvatarSDLModifier.cpp | 2 +- .../PubUtilLib/plAvatar/plAvatarTasks.cpp | 4 +- .../plAvatar/plPhysicalControllerCore.cpp | 1393 ++++++++--------- .../plAvatar/plPhysicalControllerCore.h | 448 +++--- .../plInputCore/plSceneInputInterface.cpp | 2 +- .../plModifier/plExcludeRegionModifier.cpp | 1 - .../plPhysical/plCollisionDetector.cpp | 10 +- .../Plasma/PubUtilLib/plPhysical/plSimDefs.h | 19 - 33 files changed, 990 insertions(+), 2327 deletions(-) delete mode 100644 Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp delete mode 100644 Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h delete mode 100644 Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp delete mode 100644 Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h diff --git a/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp b/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp index 024f6183..d893d216 100644 --- a/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp +++ b/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp @@ -57,7 +57,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plAvatarMgr.h" #include "plAvatar/plArmatureMod.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" bool plListener::fPrintDbgInfo = false; diff --git a/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp b/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp index 1488eaf1..852490d3 100644 --- a/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp +++ b/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp @@ -63,7 +63,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnSceneObject/plSimulationInterface.h" #include "plAvatar/plAvatarMgr.h" #include "plAvatar/plArmatureMod.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" // new stuff diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp index 0fce342f..aafb26e2 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp @@ -554,7 +554,7 @@ bool pySceneObject::IsAvatar() return false; } -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" PyObject* pySceneObject::GetAvatarVelocity() { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt index c8b82c78..f687d1f1 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt @@ -15,7 +15,6 @@ set(plAvatar_SOURCES plAGMasterSDLModifier.cpp plAGModifier.cpp plAnimStage.cpp - plAntiGravAction.cpp plArmatureEffects.cpp plArmatureMod.cpp plAvatarClothing.cpp @@ -32,7 +31,6 @@ set(plAvatar_SOURCES plAvBrainHuman.cpp plAvBrainRideAnimatedPhysical.cpp plAvBrainSwim.cpp - plAvCallbackAction.cpp plAvLadderModifier.cpp plAvTaskBrain.cpp plAvTaskSeek.cpp @@ -60,7 +58,6 @@ set(plAvatar_HEADERS plAGMasterSDLModifier.h plAGModifier.h plAnimStage.h - plAntiGravAction.h plArmatureEffects.h plArmatureMod.h plAvatarClothing.h @@ -78,7 +75,6 @@ set(plAvatar_HEADERS plAvBrainHuman.h plAvBrainRideAnimatedPhysical.h plAvBrainSwim.h - plAvCallbackAction.h plAvDefs.h plAvLadderModifier.h plAvTask.h diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp index 3b158848..f5dc5ac5 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvCallbackAction.h" // must be first: references havok new // singular #include "plAnimStage.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp deleted file mode 100644 index d5b4da3c..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp +++ /dev/null @@ -1,171 +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==*/ -#if 0 -// havok first -#include -#include - -#include "plAntiGravAction.h" - -#include "pnSceneObject/plSceneObject.h" -#include "plHavok1/plHKPhysical.h" -#include "plAvatar/plSwimRegion.h" -#include "hsTimer.h" - -// This is meant to be a specific physicsAction for the swim behavior -plAntiGravAction::plAntiGravAction(plHKPhysical *physical, plAGApplicator *rootApp) : - plAnimatedCallbackAction(physical, rootApp), - fOnGround(false), - fBuoyancy(1.f), - fSurfaceHeight(0.f), - fCurrentRegion(nil), - fHadContacts(false) -{ -} - -plSimDefs::ActionType plAntiGravAction::GetType() -{ - return plSimDefs::kAntiGravAction; -} - -void plAntiGravAction::apply(Havok::Subspace &space, Havok::hkTime time) -{ - double elapsed = time.asDouble() - getRefresh().asDouble(); - setRefresh(time); - - IAdjustBuoyancy(); - Havok::RigidBody *body = fPhysical->GetBody(); - float mass = body->getMass(); - Havok::Vector3 gravity = space.getGravity(); - Havok::Vector3 force = -gravity * (mass * fBuoyancy); - body->applyForce(force); - - hsVector3 vel; - fPhysical->GetLinearVelocitySim(vel); - fAnimPosVel.fZ = vel.fZ; - - hsVector3 linCurrent(0.f, 0.f, 0.f); - float angCurrent = 0.f; - if (fCurrentRegion != nil) - fCurrentRegion->GetCurrent(fPhysical, linCurrent, angCurrent, (float)elapsed); - - int numContacts = fPhysical->GetNumContacts(); - fHadContacts = (numContacts > 0); - - const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); - fOnGround = false; - int i; - for (i = 0; i < numContacts; i++) - { - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - float dotUp = straightUp.dot(contact->m_normal); - if (dotUp > .5) - { - fOnGround = true; - break; - } - } - - fPhysical->SetLinearVelocitySim(fAnimPosVel + linCurrent); - fPhysical->SetAngularVelocitySim(hsVector3(0.f, 0.f, fAnimAngVel + fTurnStr + angCurrent)); -} - -void plAntiGravAction::SetSurface(plSwimRegionInterface *region, float surfaceHeight) -{ - fCurrentRegion = region; - if (region != nil) - fSurfaceHeight = surfaceHeight; -} - -void plAntiGravAction::IAdjustBuoyancy() -{ - // "surface depth" refers to the depth our handle object should be below - // the surface for the avatar to be "at the surface" - static const float surfaceDepth = 4.0f; - // 1.0 = neutral buoyancy - // 0 = no buoyancy (normal gravity) - // 2.0 = opposite of gravity, floating upwards - static const float buoyancyAtSurface = 1.0f; - - if (fCurrentRegion == nil) - { - fBuoyancy = 0.f; - return; - } - - hsMatrix44 l2w, w2l; - fPhysical->GetTransform(l2w, w2l); - float depth = fSurfaceHeight - surfaceDepth - l2w.GetTranslate().fZ; - if (depth < -1) - fBuoyancy = 0.f; // Same as being above ground. Plain old gravity. - else if (depth < 0) - fBuoyancy = 1 + depth; - else - { - hsVector3 vel; - fPhysical->GetLinearVelocitySim(vel); - if (vel.fZ > 0) - { - if (vel.fZ > fCurrentRegion->fMaxUpwardVel) - { - vel.fZ = fCurrentRegion->fMaxUpwardVel; - fPhysical->SetLinearVelocitySim(vel); - } - else - { - if (depth > 1) - fBuoyancy = fCurrentRegion->fUpBuoyancy; - else - fBuoyancy = (fCurrentRegion->fUpBuoyancy - 1) * depth + 1; - } - } - else - { - if (depth > 1) - fBuoyancy = fCurrentRegion->fDownBuoyancy; - else - fBuoyancy = (fCurrentRegion->fDownBuoyancy - 1) * depth + 1; - } - } -} - -#endif \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h b/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h deleted file mode 100644 index ee4049ab..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h +++ /dev/null @@ -1,79 +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==*/ -#if 0//ndef PL_ANTI_GRAV_ACTION_H -#define PL_ANTI_GRAV_ACTION_H - -#include "plAvCallbackAction.h" - -class plSwimRegionInterface; - -class plAntiGravAction : public plAnimatedCallbackAction -{ -public: - plAntiGravAction(plHKPhysical *physical, plAGApplicator *rootApp); - - /** Return the type of the action as defined in the enum plSimDefs::ActionType. - Used to retrieve actions by entity/type indexing, and to - reuse actions that can be shared between entities. */ - virtual plSimDefs::ActionType GetType(); - - /** Called by Havok at substep frequency. */ - void apply(Havok::Subspace &s, Havok::hkTime time); - - void SetSurface(plSwimRegionInterface *region, float surfaceHeight); - float GetBuoyancy() { return fBuoyancy; } - bool IsOnGround() { return fOnGround; } - bool HadContacts() { return fHadContacts; } - -protected: - void IAdjustBuoyancy(); - - bool fOnGround; - bool fHadContacts; - float fBuoyancy; - float fSurfaceHeight; - plSwimRegionInterface *fCurrentRegion; -}; - -#endif - - diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp index 1d53edca..526710a7 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvCallbackAction.h" #include "plStatusLog/plStatusLog.h" #include "plArmatureEffects.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index 018ccb09..f155d6ef 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -54,7 +54,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvBrainHuman.h" #include "plMatrixChannel.h" #include "plAvatarTasks.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plAvBrainCritter.h" // global @@ -534,8 +534,7 @@ void plArmatureModBase::EnablePhysics(bool status, uint16_t reason /* = kDisable // i.e. normal enabled physical void plArmatureModBase::EnablePhysicsKinematic(bool status) { - if (fController) - fController->Kinematic(status); + EnablePhysics(!status, kDisableReasonKinematic); } void plArmatureModBase::EnableDrawing(bool status, uint16_t reason /* = kDisableReasonUnknown */) @@ -2670,19 +2669,7 @@ void plArmatureMod::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *str debugTxt.DrawString(x, y, strBuf); y += lineHeight; - hsPoint3 kPos; - const char *kinematic = "n.a."; const char* frozen = "n.a."; - if (fController) - { - fController->GetKinematicPosition(kPos); - kinematic = fController->IsKinematic() ? "on" : "off"; - } - sprintf(strBuf, "kinematc(world): %.2f, %.2f, %.2f Kinematic: %3s", - kPos.fX, kPos.fY, kPos.fZ,kinematic); - debugTxt.DrawString(x, y, strBuf); - y += lineHeight; - if (fController) frozen = fController->IsEnabled() ? "no" : "yes"; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h index c771a7a8..4f2a69c4 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h @@ -146,6 +146,7 @@ public: kDisableReasonCCR = 0x0008, kDisableReasonVehicle = 0x0010, kDisableReasonGenericBrain = 0x0020, + kDisableReasonKinematic = 0x0040 }; void EnablePhysics(bool status, uint16_t reason = kDisableReasonUnknown); void EnablePhysicsKinematic(bool status); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp index dcc7bfdb..71314a78 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp @@ -40,7 +40,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plAvBrainCritter.h" #include "plAvBrainHuman.h" #include "plArmatureMod.h" @@ -125,7 +125,7 @@ protected: /////////////////////////////////////////////////////////////////////////////// -plAvBrainCritter::plAvBrainCritter(): fCallbackAction(nil), fCurMode(kIdle), fNextMode(kIdle), fFadingNextBehavior(true), +plAvBrainCritter::plAvBrainCritter(): fWalkingStrategy(nil), fCurMode(kIdle), fNextMode(kIdle), fFadingNextBehavior(true), fAvoidingAvatars(false), fFinalGoalPos(0, 0, 0), fImmediateGoalPos(0, 0, 0), fDotGoal(0), fAngRight(0) { @@ -143,8 +143,8 @@ plAvBrainCritter::~plAvBrainCritter() fBehaviors[i] = nil; } - delete fCallbackAction; - fCallbackAction = nil; + delete fWalkingStrategy; + fWalkingStrategy = nil; fUserBehaviors.clear(); fReceivers.clear(); @@ -167,8 +167,8 @@ bool plAvBrainCritter::Apply(double time, float elapsed) IProcessBehavior(time, elapsed); // just continue with the currently running one // update our controller to keep us turned and moving to where we want to go - fCallbackAction->RecalcVelocity(time, time - elapsed); - fCallbackAction->SetTurnStrength(IGetTurnStrength(time)); + fWalkingStrategy->SetTurnStrength(IGetTurnStrength(time)); + fWalkingStrategy->RecalcVelocity(time, elapsed); return plArmatureBrain::Apply(time, elapsed); } @@ -188,13 +188,13 @@ void plAvBrainCritter::Activate(plArmatureModBase* avMod) IInitBaseAnimations(); // create the controller if we haven't done so already - if (!fCallbackAction) + if (!fWalkingStrategy) { plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = avMod->GetController(); - fCallbackAction = new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } // tell people that care that we are good to go @@ -224,7 +224,7 @@ void plAvBrainCritter::Resume() // fade in the idle fNextMode = kIdle; - fCallbackAction->Reset(false); + fWalkingStrategy->Reset(false); plArmatureBrain::Resume(); } diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h index 0615932e..561797be 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h @@ -47,7 +47,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnKeyedObject/plKey.h" class plArmatureMod; -class plWalkingController; +class plWalkingStrategy; class plAIMsg; class plAvBrainCritter : public plArmatureBrain @@ -134,8 +134,6 @@ public: virtual void DumpToDebugDisplay(int& x, int& y, int lineHeight, char* strBuf, plDebugText& debugTxt); - plWalkingController* GetCallbackAction() {return fCallbackAction;} - // For the console static bool fDrawDebug; @@ -159,7 +157,7 @@ protected: std::vector IAvatarsICanSee() const; std::vector IAvatarsICanHear() const; - plWalkingController* fCallbackAction; + plWalkingStrategy* fWalkingStrategy; int fCurMode; // current behavior we are running int fNextMode; // the next behavior to run (-1 if we aren't switching on next eval) bool fFadingNextBehavior; // is the next behavior supposed to blend? diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp index a3b2cacd..0712a2fa 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp @@ -42,7 +42,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // local includes #include "plAvBrainDrive.h" #include "plArmatureMod.h" -#include "plAvCallbackAction.h" // global includes #include "hsTimer.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp index 24bb67ba..a014e977 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvCallbackAction.h" // havok-contaminated file: must go first // singular #include "plAvBrainGeneric.h" @@ -94,7 +93,6 @@ plAvBrainGeneric::plAvBrainGeneric() fFadeIn(0.0f), fFadeOut(0.0f), fMoveMode(kMoveRelative), - fCallbackAction(nil), fBodyUsage(plAGAnim::kBodyUnknown) { } @@ -122,7 +120,6 @@ plAvBrainGeneric::plAvBrainGeneric(plAnimStageVec *stages, fFadeIn(fadeIn), fFadeOut(fadeOut), fMoveMode(moveMode), - fCallbackAction(nil), fBodyUsage(plAGAnim::kBodyUnknown) { } @@ -141,7 +138,6 @@ plAvBrainGeneric::plAvBrainGeneric(uint32_t exitFlags, float fadeIn, float fadeO fFadeIn(fadeIn), fFadeOut(fadeOut), fMoveMode(moveMode), - fCallbackAction(nil), fBodyUsage(plAGAnim::kBodyUnknown) { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h index 4a719153..305074aa 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h @@ -49,7 +49,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class plAnimStage; class plAnimStageVec; class plAvBrainGenericMsg; -class plHorizontalFreezeAction; class plNotifyMsg; /** \class plAvBrainGeneric @@ -300,7 +299,6 @@ protected: int fCurStage; // which stage are we playing? (zero-based) BrainType fType; // what type of brain are we? uint32_t fExitFlags; // what will cause us to exit? - plHorizontalFreezeAction *fCallbackAction; bool fForward; // are we currently moving forward or backward through the stages? // this is used by the "auto-" movement types in the stages diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp index fe7f4923..05422c7b 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp @@ -40,9 +40,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ -#include "plAvCallbackAction.h" // subclasses a havok object; must be in first include section #include "HeadSpin.h" +#include "plPhysicalControllerCore.h" #include "plAvBrainHuman.h" #include "plAvBrainClimb.h" #include "plAvBrainDrive.h" @@ -140,7 +140,7 @@ plAvBrainHuman::TurnCurve plAvBrainHuman::GetTurnCurve(bool walk) plAvBrainHuman::plAvBrainHuman(bool isActor /* = false */) : fHandleAGMod(nil), fStartedTurning(-1.0f), - fCallbackAction(nil), + fWalkingStrategy(nil), fPreconditions(0), fIsActor(isActor) { @@ -154,9 +154,9 @@ bool plAvBrainHuman::Apply(double timeNow, float elapsed) #endif // SetTurnStrength runs first to make sure it's set to a sane value // (or cleared). RunStandardBehaviors may overwrite it. - fCallbackAction->SetTurnStrength(IGetTurnStrength(timeNow)); + fWalkingStrategy->SetTurnStrength(IGetTurnStrength(timeNow)); RunStandardBehaviors(timeNow, elapsed); - fCallbackAction->RecalcVelocity(timeNow, timeNow - elapsed, (fPreconditions & plHBehavior::kBehaviorTypeNeedsRecalcMask)); + fWalkingStrategy->RecalcVelocity(timeNow, elapsed, (fPreconditions & plHBehavior::kBehaviorTypeNeedsRecalcMask)); plArmatureBrain::Apply(timeNow, elapsed); #ifndef _DEBUG @@ -177,13 +177,13 @@ void plAvBrainHuman::Activate(plArmatureModBase *avMod) IInitBoneMap(); IInitAnimations(); - if (!fCallbackAction) + if (!fWalkingStrategy) { plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = avMod->GetController(); - fCallbackAction = new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } @@ -328,8 +328,8 @@ plAvBrainHuman::~plAvBrainHuman() delete fBehaviors[i]; fBehaviors.Reset(); - delete fCallbackAction; - fCallbackAction = nil; + delete fWalkingStrategy; + fWalkingStrategy = nil; } void plAvBrainHuman::Deactivate() @@ -361,7 +361,7 @@ void plAvBrainHuman::Resume() if (fAvMod->GetInputFlag(S_PUSH_TO_TALK)) IChatOn(); - fCallbackAction->Reset(false); + fWalkingStrategy->Reset(false); plArmatureBrain::Resume(); } @@ -465,25 +465,23 @@ bool plAvBrainHuman::MsgReceive(plMessage * msg) { if(ride->Entering()) { - //plAvBrainRideAnimatedPhysical *rideBrain = new plAvBrainRideAnimatedPhysical(); - //fAvMod->PushBrain(rideBrain); - delete fCallbackAction; + // Switch to dynamic walking strategy + delete fWalkingStrategy; plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = fAvMod->GetController(); - fCallbackAction= new plRidingAnimatedPhysicalController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); - + fWalkingStrategy = new plDynamicWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } else { - delete fCallbackAction; + // Restore default walking strategy + delete fWalkingStrategy; plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = fAvMod->GetController(); - fCallbackAction= new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); - //hsStatusMessage("Got an exiting ride animated physical message"); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } } @@ -557,8 +555,8 @@ float plAvBrainHuman::IGetTurnStrength(double timeNow) // Turning based on keypress if ((turnLeftStrength > 0.f) || (turnRightStrength > 0.f) - || (!fCallbackAction->IsOnGround() - && !fCallbackAction->IsControlledFlight()) + || (!fWalkingStrategy->IsOnGround() + && !fWalkingStrategy->IsControlledFlight()) ) { float t = (float)(timeNow - fStartedTurning); float turnSpeed; @@ -590,7 +588,7 @@ float plAvBrainHuman::IGetTurnStrength(double timeNow) result += fAvMod->GetKeyTurnStrength() * turnSpeed; } - if (!fCallbackAction->IsControlledFlight()) + if (!fWalkingStrategy->IsControlledFlight()) result += fAvMod->GetAnalogTurnStrength() * maxTurnSpeed; return result; @@ -647,7 +645,7 @@ void plAvBrainHuman::ResetIdle() void plAvBrainHuman::IdleOnly(bool instantOff) { - if (!fCallbackAction) + if (!fWalkingStrategy) return; float rate = instantOff ? 0.f : 1.f; @@ -673,7 +671,7 @@ bool plAvBrainHuman::IsMovementZeroBlend() void plAvBrainHuman::TurnToPoint(hsPoint3 point) { - if (!fCallbackAction->IsOnGround() || IsRunningTask() || fAvMod->GetCurrentBrain() != this || !IsMovementZeroBlend()) + if (!fWalkingStrategy->IsOnGround() || IsRunningTask() || fAvMod->GetCurrentBrain() != this || !IsMovementZeroBlend()) return; hsPoint3 avPos; @@ -869,23 +867,23 @@ void plAvBrainHuman::Spawn(double timeNow) bool plAvBrainHuman::LeaveAge() { plPhysicalControllerCore* controller = fAvMod->GetController(); - if(!controller->BehavingLikeAnAnimatedPhysical()) + + // If our current walking strategy is dynamic, restore the default kinematic strategy. + if (!fWalkingStrategy->IsKinematic()) { - controller->BehaveLikeAnimatedPhysical(true); - delete fCallbackAction; + delete fWalkingStrategy; plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); - fCallbackAction= new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); } + + fWalkingStrategy->Reset(true); + plArmatureBrain::LeaveAge(); - - // pin the physical so it doesn't fall when the world is deleted fAvMod->EnablePhysics(false); - // this will get set to true when we hit ground - fCallbackAction->Reset(true); + return false; } @@ -895,11 +893,10 @@ void plAvBrainHuman::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *st debugTxt.DrawString(x, y, strBuf); y += lineHeight; - const char *grounded = fCallbackAction->IsOnGround() ? "yes" : "no"; - const char *falseGrounded = fCallbackAction->IsOnFalseGround() ? "yes" : "no"; - const char *pushing = (fCallbackAction->GetPushingPhysical() ? (fCallbackAction->GetFacingPushingPhysical() ? "facing" : "behind") : "none"); - sprintf(strBuf, "Ground: %3s, FalseGround: %3s, AirTime: %5.2f (Peak: %5.2f), PushingPhys: %6s", - grounded, falseGrounded, fCallbackAction->GetAirTime(), fCallbackAction->GetImpactTime(), pushing); + const char *grounded = fWalkingStrategy->IsOnGround() ? "yes" : "no"; + const char *pushing = (fWalkingStrategy->GetPushingPhysical() ? (fWalkingStrategy->GetFacingPushingPhysical() ? "facing" : "behind") : "none"); + sprintf(strBuf, "Ground: %3s, AirTime: %5.2f (Peak: %5.2f), PushingPhys: %6s", + grounded, fWalkingStrategy->GetAirTime(), fWalkingStrategy->GetImpactTime(), pushing); debugTxt.DrawString(x, y, strBuf); y += lineHeight; @@ -1012,8 +1009,8 @@ bool Run::PreCondition(double time, float elapsed) { if (fAnim) { - if (fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown() && fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1023,8 +1020,8 @@ bool Walk::PreCondition(double time, float elapsed) { if (fAnim) { - if (fAvMod->ForwardKeyDown() && !fAvMod->FastKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fAvMod->ForwardKeyDown() && !fAvMod->FastKeyDown() && fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1034,8 +1031,8 @@ bool WalkBack::PreCondition(double time, float elapsed) { if (fAnim) { - if (fAvMod->BackwardKeyDown() && !fAvMod->ForwardKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fAvMod->BackwardKeyDown() && !fAvMod->ForwardKeyDown() && fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1048,7 +1045,7 @@ bool StepLeft::PreCondition(double time, float elapsed) return ((fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && !(fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - fHuBrain->fCallbackAction->IsOnGround()); + fHuBrain->fWalkingStrategy->IsOnGround()); } return false; } @@ -1060,7 +1057,7 @@ bool StepRight::PreCondition(double time, float elapsed) return ((fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && !(fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - fHuBrain->fCallbackAction->IsOnGround()); + fHuBrain->fWalkingStrategy->IsOnGround()); } return false; } @@ -1101,8 +1098,8 @@ bool MovingTurnLeft::PreCondition(double time, float elapsed) { if (fAvMod->GetTurnStrength() > 0) { - if (fHuBrain->fCallbackAction->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fHuBrain->fWalkingStrategy->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1112,8 +1109,8 @@ bool MovingTurnRight::PreCondition(double time, float elapsed) { if (fAvMod->GetTurnStrength() < 0) { - if (fHuBrain->fCallbackAction->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fHuBrain->fWalkingStrategy->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } @@ -1122,14 +1119,14 @@ bool MovingTurnRight::PreCondition(double time, float elapsed) void Jump::IStart() { - fHuBrain->fCallbackAction->EnableControlledFlight(true); + fHuBrain->fWalkingStrategy->EnableControlledFlight(true); plHBehavior::IStart(); } void Jump::IStop() { - fHuBrain->fCallbackAction->EnableControlledFlight(false); + fHuBrain->fWalkingStrategy->EnableControlledFlight(false); plHBehavior::IStop(); } @@ -1140,7 +1137,7 @@ bool StandingJump::PreCondition(double time, float elapsed) { if (GetStrength() > 0.f) { - if (!fHuBrain->fCallbackAction->IsControlledFlight() || + if (!fHuBrain->fWalkingStrategy->IsControlledFlight() || fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) { return false; @@ -1152,7 +1149,7 @@ bool StandingJump::PreCondition(double time, float elapsed) if (fAvMod->JumpKeyDown() && !fAvMod->ForwardKeyDown() && fAnim->GetBlend() == 0.0f && - fHuBrain->fCallbackAction->IsOnGround()) + fHuBrain->fWalkingStrategy->IsOnGround()) { if (fAvMod->ConsumeJump()) return true; @@ -1168,7 +1165,7 @@ bool WalkingJump::PreCondition(double time, float elapsed) { if (GetStrength() > 0.f) { - if (!fHuBrain->fCallbackAction->IsControlledFlight() || + if (!fHuBrain->fWalkingStrategy->IsControlledFlight() || fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) { return false; @@ -1181,8 +1178,8 @@ bool WalkingJump::PreCondition(double time, float elapsed) !fAvMod->FastKeyDown() && fAvMod->ForwardKeyDown() && fAnim->GetBlend() == 0.0f && - fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) { if (fAvMod->ConsumeJump()) return true; @@ -1198,7 +1195,7 @@ bool RunningJump::PreCondition(double time, float elapsed) { if (GetStrength() > 0.f) { - if (!fHuBrain->fCallbackAction->IsControlledFlight() || + if (!fHuBrain->fWalkingStrategy->IsControlledFlight() || fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) { return false; @@ -1211,8 +1208,8 @@ bool RunningJump::PreCondition(double time, float elapsed) fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown() && fAnim->GetBlend() == 0.0f && - fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) { if (fAvMod->ConsumeJump()) return true; @@ -1237,13 +1234,13 @@ bool RunningImpact::PreCondition(double time, float elapsed) { if (fDuration > 0.0f) fDuration = fDuration - elapsed; - else if (fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->GetImpactTime() > kMinAirTime) + else if (fHuBrain->fWalkingStrategy->IsOnGround() && fHuBrain->fWalkingStrategy->GetImpactTime() > kMinAirTime) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fZ < -kMinImpactVel) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ < -kMinImpactVel) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fY < kRunningImpactThresh) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fY < kRunningImpactThresh) { - fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fCallbackAction->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); + fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); if (fMaxBlend > 1) fMaxBlend = 1; fDuration = 1.0f / fFadeIn; @@ -1267,13 +1264,13 @@ bool GroundImpact::PreCondition(double time, float elapsed) bool result = false; if (fDuration > 0.0f) fDuration = fDuration - elapsed; - else if (fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->GetImpactTime() > kMinAirTime) + else if (fHuBrain->fWalkingStrategy->IsOnGround() && fHuBrain->fWalkingStrategy->GetImpactTime() > kMinAirTime) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fZ < -kMinImpactVel) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ < -kMinImpactVel) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fY >= kRunningImpactThresh) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fY >= kRunningImpactThresh) { - fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fCallbackAction->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); + fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); if (fMaxBlend > 1) fMaxBlend = 1; fDuration = 1.0f / fFadeIn; @@ -1292,7 +1289,7 @@ void GroundImpact::IStop() bool Fall::PreCondition(double time, float elapsed) { - return !fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->HitGroundInThisAge(); + return !fHuBrain->fWalkingStrategy->IsOnGround() && fHuBrain->fWalkingStrategy->HitGroundInThisAge(); } void Fall::Process(double time, float elapsed) @@ -1304,7 +1301,7 @@ void Fall::Process(double time, float elapsed) if (fAnim && fAnim->GetBlend() > 0.8) { float panicThresh = plAvBrainHuman::kAirTimePanicThreshold; - if (panicThresh > 0.0f && fHuBrain->fCallbackAction->GetAirTime() > panicThresh) + if (panicThresh > 0.0f && fHuBrain->fWalkingStrategy->GetAirTime() > panicThresh) { fHuBrain->IdleOnly(); // clear the fall state; we're going somewhere new fAvMod->PanicLink(); @@ -1321,7 +1318,7 @@ void Push::Process(double time, float elapsed) fAvMod->GetPositionAndRotationSim(&pos, &rot); hsPoint3 lookAt; - fHuBrain->fCallbackAction->GetPushingPhysical()->GetPositionSim(lookAt); + fHuBrain->fWalkingStrategy->GetPushingPhysical()->GetPositionSim(lookAt); hsVector3 up(0.f, 0.f, 1.f); float angle = atan2(lookAt.fY - pos.fY, lookAt.fX - pos.fX) + M_PI / 2; hsQuat targRot(angle, &up); @@ -1335,23 +1332,23 @@ void Push::Process(double time, float elapsed) globFwd = rot.Rotate(&globFwd); if (globFwd.fX < 0) - fHuBrain->fCallbackAction->SetTurnStrength(-turnSpeed); + fHuBrain->fWalkingStrategy->SetTurnStrength(-turnSpeed); else - fHuBrain->fCallbackAction->SetTurnStrength(turnSpeed); + fHuBrain->fWalkingStrategy->SetTurnStrength(turnSpeed); } //bool PushIdle::PreCondition(double time, float elapsed) //{ -// return (fHuBrain->fCallbackAction->GetPushingPhysical() && -// fHuBrain->fCallbackAction->IsOnGround() && +// return (fHuBrain->fWalkingStrategy->GetPushingPhysical() && +// fHuBrain->fWalkingStrategy->IsOnGround() && // !fAvMod->TurnLeftKeyDown() && !fAvMod->TurnRightKeyDown() // && fAvMod->GetTurnStrength() == 0); //} bool PushWalk::PreCondition(double time, float elapsed) { - return (fHuBrain->fCallbackAction->GetPushingPhysical() && fHuBrain->fCallbackAction->GetFacingPushingPhysical() && - fHuBrain->fCallbackAction->IsOnGround() && + return (fHuBrain->fWalkingStrategy->GetPushingPhysical() && fHuBrain->fWalkingStrategy->GetFacingPushingPhysical() && + fHuBrain->fWalkingStrategy->IsOnGround() && fAvMod->ForwardKeyDown()); } @@ -1366,7 +1363,7 @@ bool PushSimpleMultiStage(plArmatureMod *avatar, const char *enterAnim, const ch { plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(avatar->FindBrainByClass(plAvBrainHuman::Index())); const char *names[3] = {enterAnim, idleAnim, exitAnim}; - if (!huBrain || !huBrain->fCallbackAction->IsOnGround() || !huBrain->fCallbackAction->HitGroundInThisAge() || huBrain->IsRunningTask() || + if (!huBrain || !huBrain->fWalkingStrategy->IsOnGround() || !huBrain->fWalkingStrategy->HitGroundInThisAge() || huBrain->IsRunningTask() || !avatar->IsPhysicsEnabled() || avatar->FindMatchingGenericBrain(names, 3)) return false; @@ -1429,7 +1426,7 @@ bool AvatarEmote(plArmatureMod *avatar, const char *emoteName) if (swimBrain && swimBrain->IsSwimming()) return false; - if (huBrain && huBrain->fCallbackAction->IsOnGround() && huBrain->fCallbackAction->HitGroundInThisAge() && !huBrain->IsRunningTask() && + if (huBrain && huBrain->fWalkingStrategy->IsOnGround() && huBrain->fWalkingStrategy->HitGroundInThisAge() && !huBrain->IsRunningTask() && emote && !alreadyActive && avatar->IsPhysicsEnabled()) { plKey avKey = avatar->GetKey(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h index 69a40717..158b89ab 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h @@ -58,7 +58,7 @@ class plAGAnimInstance; class plAvTask; class plAvTaskMsg; class plAvBrainHuman; -class plWalkingController; +class plWalkingStrategy; class plArmatureUpdateMsg; class plClimbMsg; class plControlEventMsg; @@ -164,7 +164,7 @@ public: static const float kControlledFlightThreshold; static const float kAirTimeThreshold; static const float kAirTimePanicThreshold; - plWalkingController* fCallbackAction; + plWalkingStrategy* fWalkingStrategy; protected: plAGAnim *FindCustomAnim(const char *baseName); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp index d07ce063..4bf9661c 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp @@ -44,7 +44,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvBrainHuman.h" #include "plAvBrain.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plMessage/plRideAnimatedPhysMsg.h" @@ -52,20 +52,19 @@ void plAvBrainRideAnimatedPhysical::Activate(plArmatureModBase *avMod) { plArmatureBrain::Activate(avMod); IInitAnimations(); - if (!fCallbackAction) + if (!fWalkingStrategy) { plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = avMod->GetController(); - fCallbackAction = new plRidingAnimatedPhysicalController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plDynamicWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } } plAvBrainRideAnimatedPhysical::~plAvBrainRideAnimatedPhysical() { - delete fCallbackAction; - fCallbackAction=nil; - + delete fWalkingStrategy; + fWalkingStrategy = nil; } void plAvBrainRideAnimatedPhysical::Deactivate() diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h index c1c3f578..b5e923a1 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h @@ -41,8 +41,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plAvBrainHuman.h" -class plRidingAnimatedPhysicalController; - class plAvBrainRideAnimatedPhysical : public plAvBrainHuman { public: diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp index b447d820..9046f298 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp @@ -46,10 +46,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // ///////////////////////////////////////////////////////////////////////////////////////// -//#include -//#include -#include "plAntiGravAction.h" // descends from Havok class, so must be included first, like havok objects - // singular #include "plAvBrainSwim.h" @@ -69,7 +65,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsTimer.h" #include "plPhysical.h" #include "plPhysicalControllerCore.h" -#include "plAvCallbackAction.h" // other #include "plPhysical/plCollisionDetector.h" #include "plPipeline/plDebugText.h" @@ -180,18 +175,16 @@ public: static const float timeToMaxTurn = 0.5f; static const float incPerSec = maxTurnSpeed / timeToMaxTurn; -// hsAssert(0, "fixme physx"); - float oldSpeed = fabs(fSwimBrain->fCallbackAction->GetTurnStrength()); + float oldSpeed = fabs(fSwimBrain->fSwimStrategy->GetTurnStrength()); float thisInc = elapsed * incPerSec; float newSpeed = min(oldSpeed + thisInc, maxTurnSpeed); - fSwimBrain->fCallbackAction->SetTurnStrength(newSpeed * fAvMod->GetKeyTurnStrength() + fAvMod->GetAnalogTurnStrength()); + fSwimBrain->fSwimStrategy->SetTurnStrength(newSpeed * fAvMod->GetKeyTurnStrength() + fAvMod->GetAnalogTurnStrength()); // the turn is actually applied during PhysicsUpdate } virtual void IStop() { -// hsAssert(0, "fixme physx"); - if (fSwimBrain->fCallbackAction) - fSwimBrain->fCallbackAction->SetTurnStrength(0.0f); + if (fSwimBrain->fSwimStrategy) + fSwimBrain->fSwimStrategy->SetTurnStrength(0.0f); plSwimBehavior::IStop(); } }; @@ -237,7 +230,7 @@ public: const float plAvBrainSwim::kMinSwimDepth = 4.0f; plAvBrainSwim::plAvBrainSwim() : - fCallbackAction(nil), + fSwimStrategy(nil), fMode(kWalking), fSurfaceDistance(0.f) { @@ -250,12 +243,9 @@ plAvBrainSwim::plAvBrainSwim() : plAvBrainSwim::~plAvBrainSwim() { - if(fCallbackAction) - { - IDetachAction(); - delete fCallbackAction; - fCallbackAction=nil; - } + delete fSwimStrategy; + fSwimStrategy = nil; + fSurfaceProbeMsg->UnRef(); int i; @@ -273,8 +263,7 @@ bool plAvBrainSwim::Apply(double time, float elapsed) fMode = kWading; plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(fAvMod->GetNextBrain(this)); -// hsAssert(0, "fixme physx"); - if (huBrain && !huBrain->fCallbackAction->IsOnGround()) + if (huBrain && !huBrain->fWalkingStrategy->IsOnGround()) { // We're jumping in! Trigger splash effect (sound) plArmatureEffectMsg *msg = new plArmatureEffectMsg(fAvMod->GetArmatureEffects()->GetKey(), kTime); @@ -318,8 +307,7 @@ bool plAvBrainSwim::Apply(double time, float elapsed) // The contact check is so that if buoyancy bobs us a little too high, we don't // switch to wading only to fall again. -// hsAssert(0, "fixme physx"); - if (fSurfaceDistance < kMinSwimDepth-.5 && fCallbackAction->HadContacts()) + if (fSurfaceDistance < kMinSwimDepth-.5 && fSwimStrategy->HadContacts()) IStartWading(); } else if (fMode == kSwimming3D) @@ -346,13 +334,12 @@ bool plAvBrainSwim::MsgReceive(plMessage *msg) else fSurfaceDistance = -100.f; -// hsAssert(0, "fixme physx"); - if (fCallbackAction) + if (fSwimStrategy) { if (region) - fCallbackAction->SetSurface(region, fArmature->GetTarget(0)->GetLocalToWorld().GetTranslate().fZ + fSurfaceDistance); + fSwimStrategy->SetSurface(region, fArmature->GetTarget(0)->GetLocalToWorld().GetTranslate().fZ + fSurfaceDistance); else - fCallbackAction->SetSurface(nil, 0.f); + fSwimStrategy->SetSurface(nil, 0.f); } return true; } @@ -419,20 +406,16 @@ void plAvBrainSwim::Activate(plArmatureModBase* avMod) void plAvBrainSwim::Deactivate() { plArmatureBrain::Deactivate(); - - IDetachAction(); } void plAvBrainSwim::Suspend() { - if (fMode == kSwimming2D) - IDetachAction(); } void plAvBrainSwim::Resume() { if (fMode == kSwimming2D) - IAttachAction(); + fSwimStrategy->Reset(false); } bool plAvBrainSwim::IsWalking() @@ -460,8 +443,6 @@ void plAvBrainSwim::IStartWading() for (i = 0; i < fBehaviors.GetCount(); i++) fBehaviors[i]->SetStrength(0.f, 2.f); - IDetachAction(); - if (fAvMod->IsLocalAvatar()) { plCameraMsg* pMsg = new plCameraMsg; @@ -479,7 +460,16 @@ void plAvBrainSwim::IStartSwimming(bool is2D) plArmatureBrain *nextBrain = fAvMod->GetNextBrain(this); nextBrain->Suspend(); - IAttachAction(); + if (!fSwimStrategy) + { + plSceneObject * avObj = fArmature->GetTarget(0); + plAGModifier *agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + plPhysicalControllerCore *controller = fAvMod->GetController(); + fSwimStrategy = new plSwimStrategy(agMod->GetApplicator(kAGPinTransform), controller); + } + + fSwimStrategy->Reset(false); + if (is2D) fMode = kSwimming2D; else @@ -509,8 +499,8 @@ bool plAvBrainSwim::IProcessSwimming2D(double time, float elapsed) else behavior->SetStrength(0.f, 2.f); } -// hsAssert(0, "fixme physx"); - fCallbackAction->RecalcVelocity(time, time - elapsed); + + fSwimStrategy->RecalcVelocity(time, elapsed); return true; } @@ -568,59 +558,6 @@ bool plAvBrainSwim::IInitAnimations() return true; } -bool plAvBrainSwim::IAttachAction() -{ - bool result = false; - if(fAvMod) - { -// hsAssert(0, "fixme physx"); - plPhysicalControllerCore *physical = fAvMod->GetController(); - - if (physical) - { - if (!fCallbackAction) - { - plSceneObject * avObj = fArmature->GetTarget(0); - plAGModifier *agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); - fCallbackAction = new plSwimmingController(avObj, agMod->GetApplicator(kAGPinTransform),physical); -// physical->AttachAction(fCallbackAction, true, false); - result = true; - } - else - { - fCallbackAction->ActivateController(); - } - - } - } - return result; -} - -bool plAvBrainSwim::IDetachAction() -{ - bool result = false; - - if (fCallbackAction) - { -// hsAssert(0, "fixme physx"); -// plPhysical *physical = fAvMod->GetPhysical(); -// -// if(physical) -// { -// physical->RemoveAction(fCallbackAction); -// result = true; // there was an action and we removed it -// } - - // TODO: We get a compiler warning about deleting a pointer to an - // undefined class. So, who knows what the code is actually doing. - // Seems bad. Just putting a note in here for whoever fixes the - // physx issue. - //delete fCallbackAction; - //fCallbackAction = nil; - } - return result; -} - void plAvBrainSwim::IProbeSurface() { hsPoint3 ourPos = fAvMod->GetTarget(0)->GetLocalToWorld().GetTranslate(); @@ -676,21 +613,15 @@ void plAvBrainSwim::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *str debugTxt.DrawString(x, y, strBuf); y += lineHeight; + float buoy = fSwimStrategy ? fSwimStrategy->GetBuoyancy() : 0.0f; + sprintf(strBuf, "Distance to surface: %f Buoyancy: %f", fSurfaceDistance, buoy); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; -// hsAssert(0, "fixme physx"); -// float buoy = fCallbackAction? fCallbackAction->GetBuoyancy() : 0.0f; -// sprintf(strBuf, "Distance to surface: %f Buoyancy: %f", fSurfaceDistance, buoy); -// debugTxt.DrawString(x, y, strBuf); -// y += lineHeight; -// -// hsVector3 linV; -// fAvMod->GetPhysical()->GetLinearVelocitySim(linV); -// hsVector3 angV; -// fAvMod->GetPhysical()->GetAngularVelocitySim(angV); -// float angle = angV.fZ > 0 ? angV.Magnitude() : -angV.Magnitude(); -// sprintf(strBuf, "Velocity: Linear (%5.2f, %5.2f, %5.2f), Angular %5.2f", linV.fX, linV.fY, linV.fZ, angle); -// debugTxt.DrawString(x, y, strBuf); -// y += lineHeight; + hsVector3 linV = fAvMod->GetController()->GetAchievedLinearVelocity(); + sprintf(strBuf, "Linear Velocity: (%5.2f, %5.2f, %5.2f)", linV.fX, linV.fY, linV.fZ); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; int i; for (i = 0; i < fBehaviors.GetCount(); i++) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h index d3eec23d..6de11ee9 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h @@ -51,7 +51,7 @@ class plAntiGravAction; class plControlEventMsg; class plLOSRequestMsg; class plSwimRegionInterface; -class plSwimmingController; +class plSwimStrategy; class plAvBrainSwim : public plArmatureBrain { public: @@ -73,7 +73,7 @@ public: bool IsSwimming(); float GetSurfaceDistance() { return fSurfaceDistance; } - plSwimmingController *fCallbackAction; + plSwimStrategy *fSwimStrategy; static const float kMinSwimDepth; protected: @@ -86,8 +86,6 @@ protected: bool IProcessBehaviors(double time, float elapsed); virtual bool IInitAnimations(); - bool IAttachAction(); - bool IDetachAction(); void IProbeSurface(); bool IHandleControlMsg(plControlEventMsg* msg); float IGetTargetZ(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp deleted file mode 100644 index 76e22f5f..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp +++ /dev/null @@ -1,579 +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 "plAvCallbackAction.h" -#include "plMessage/plLOSHitMsg.h" - -#include "plArmatureMod.h" // for LOS enum type -#include "plMatrixChannel.h" -#include "hsTimer.h" -#include "plPhysicalControllerCore.h" - -// Generic geom utils. -static bool LinearVelocity(hsVector3 &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); -static void AngularVelocity(float &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); -static float AngleRad2d (float x1, float y1, float x3, float y3); -inline hsVector3 GetYAxis(hsMatrix44 &mat) -{ - return hsVector3(mat.fMap[1][0], mat.fMap[1][1], mat.fMap[1][2]); -} - -plAnimatedController::plAnimatedController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) - : fRootObject(rootObject) - , fRootApp(rootApp) - , fController(controller) - , fTurnStr(0.f) - , fAnimAngVel(0.f) - , fAnimPosVel(0.f, 0.f, 0.f) -{ -} - -void plAnimatedController::RecalcVelocity(double timeNow, double timePrev, bool useAnim /* = true */) -{ - if (useAnim) - { - // while you may think it would be correct to cache this, - // what we're actually asking is "what would the animation's - // position be at the previous time given its *current* - // parameters (particularly blends)" - hsMatrix44 prevMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timePrev, true); - hsMatrix44 curMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timeNow, true); - - // If we get a valid linear velocity (ie, we didn't wrap around in the anim), - // use it. Otherwise just reuse the previous frames velocity. - hsVector3 linearVel; - if (LinearVelocity(linearVel, (float)(timeNow - timePrev), prevMat, curMat)) - fAnimPosVel = linearVel; - - // Automatically sets fAnimAngVel - AngularVelocity(fAnimAngVel, (float)(timeNow - timePrev), prevMat, curMat); - } - else - { - fAnimPosVel.Set(0.f, 0.f, 0.f); - fAnimAngVel = 0.f; - } - - if (fController) - fController->SetVelocities(fAnimPosVel, fAnimAngVel + fTurnStr); -} - -/////////////////////////////////////////////////////////////////////////// - -const float plWalkingController::kControlledFlightThreshold = 1.f; // seconds - -plWalkingController::plWalkingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) - : plAnimatedController(rootObject, rootApp, controller) - , fHitGroundInThisAge(false) - , fWaitingForGround(false) - , fControlledFlightTime(0) - , fControlledFlight(0) - , fImpactTime(0.f) - , fImpactVelocity(0.f, 0.f, 0.f) - , fClearImpact(false) - , fGroundLastFrame(false) -{ - if (fController) - { - fWalkingStrategy= new plWalkingStrategy(fController); - fController->SetMovementSimulationInterface(fWalkingStrategy); - } - else - fWalkingStrategy = nil; -} - -void plWalkingController::RecalcVelocity(double timeNow, double timePrev, bool useAnim) -{ - if (!fHitGroundInThisAge && fController && fController->IsEnabled() && fWalkingStrategy->IsOnGround()) - fHitGroundInThisAge = true; // if we're not pinned and we're not in an age yet, we are now. - - if (fClearImpact) - { - fImpactTime = 0.f; - fImpactVelocity.Set(0.f, 0.f, 0.f); - } - - if (fController && !fWalkingStrategy->IsOnGround()) - { - // PhysX Hack: AchievedLinearVelocity is a Cyanic fix for superjumps. LinearVelocity is - // always (0,0,0) outside of the controller update proc - fImpactTime = fWalkingStrategy->GetAirTime(); - fImpactVelocity = fController->GetAchievedLinearVelocity(); - // convert orientation from subworld to avatar-local coordinates - fImpactVelocity = (hsVector3)fController->GetLocalRotation().Rotate(&fImpactVelocity); - fClearImpact = false; - } - else - fClearImpact = true; - - if (IsControlledFlight()) - { - if (fWalkingStrategy && fWalkingStrategy->IsOnGround()) - fControlledFlightTime = fWalkingStrategy->GetAirTime(); - if(fGroundLastFrame&&(fWalkingStrategy && !fWalkingStrategy->IsOnGround())) - { - //we have started to leave the ground tell the movement strategy in case it cares - fWalkingStrategy->StartJump(); - } - if (fControlledFlightTime > kControlledFlightThreshold) - EnableControlledFlight(false); - } - if (fWalkingStrategy) - fGroundLastFrame = fWalkingStrategy->IsOnGround(); - else - fGroundLastFrame=false; - plAnimatedController::RecalcVelocity(timeNow, timePrev, useAnim); -} - -void plWalkingController::Reset(bool newAge) -{ - - ActivateController(); - if (newAge) - { - if (fWalkingStrategy) - fWalkingStrategy->ResetAirTime(); - fHitGroundInThisAge = false; - } -} - void plWalkingController::ActivateController() -{ - if (fWalkingStrategy) - { - fWalkingStrategy->RefreshConnectionToControllerCore(); - } - else - { - fWalkingStrategy= new plWalkingStrategy(fController); - fWalkingStrategy->RefreshConnectionToControllerCore(); - - } -} - -bool plWalkingController::EnableControlledFlight(bool status) -{ - if (status) - { - if (fControlledFlight == 0) - fControlledFlightTime = 0.f; - - ++fControlledFlight; - fWaitingForGround = true; - } - else - fControlledFlight = max(--fControlledFlight, 0); - - return status; -} -plWalkingController::~plWalkingController() -{ - delete fWalkingStrategy; - if (fController) - fController->SetMovementSimulationInterface(nil); -} -#if 0 -void plWalkingController::Update() -{ -// double elapsed = time.asDouble() - getRefresh().asDouble(); -// setRefresh(time); -// -// bool isPhysical = !fPhysical->GetProperty(plSimulationInterface::kPinned); -// const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); -// bool alreadyInAge = fHitGroundInThisAge; -// -// int numContacts = fPhysical->GetNumContacts(); -// bool ground = false; -// fPushingPhysical = nil; -// int i, j; - -/* for(i = 0; i < numContacts; i++) - { - plHKPhysical *contactPhys = fPhysical->GetContactPhysical(i); - if (!contactPhys) - continue; // Physical no longer exists. Skip it. - - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - float dotUp = straightUp.dot(contact->m_normal); - if (dotUp > .5) - ground = true; - else if (contactPhys->GetProperty(plSimulationInterface::kAvAnimPushable)) - { - hsPoint3 position; - hsQuat rotation; - fPhysical->GetPositionAndRotationSim(&position, &rotation); - - hsQuat inverseRotation = rotation.Inverse(); - hsVector3 normal(contact->m_normal.x, contact->m_normal.y, contact->m_normal.z); - fFacingPushingPhysical = (inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false); - - fPushingPhysical = contactPhys; - } - } - - // 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. - const float threshold = hsDegreesToRadians(240); - if (!ground && numContacts >= 2) - { - // Can probably do a special case for exactly 2 contacts. Not sure if it's worth it... - - fCollisionAngles.SetCount(numContacts); - for (i = 0; i < numContacts; i++) - { - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - fCollisionAngles[i] = atan2(contact->m_normal.y, contact->m_normal.x); - } - - // 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]) - { - float 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 * M_PI))) - ground = true; - } - } -*/ - - bool ground = fController ? fController->GotGroundHit() : true; - bool isPhysical = true; - - if (!fHitGroundInThisAge && isPhysical) - fHitGroundInThisAge = true; // if we're not pinned and we're not in an age yet, we are now. - - if (IsControlledFlight()) - fControlledFlightTime += (float)elapsed; - if (fControlledFlightTime > kControlledFlightThreshold && numContacts > 0) - EnableControlledFlight(false); - - if (ground || !isPhysical) - { - if (!IsControlledFlight() && !IsOnGround()) - { - // The first ground contact in an age doesn't count. -// if (alreadyInAge) -// { -// hsVector3 vel; -// fPhysical->GetLinearVelocitySim(vel); -// fImpactVel = vel.fZ; -// fTimeInAirPeak = (float)(fTimeInAir + elapsed); -// } - - fWaitingForGround = false; - } - fTimeInAir = 0; - } - else if (elapsed < plSimulationMgr::GetInstance()->GetMaxDelta()) - { - // If the simultation skipped a huge chunk of time, we didn't process the - // collisions, which could trick us into thinking we've just gone a long - // time without hitting ground. So we only count the time if this wasn't - // the case. - fTimeInAir += (float)elapsed; - } - - - // Tweakage so that we still fall under the right conditions. - // If we're in controlled flight, or standing still with ground solidly under us (probe hit). We only use anim velocity. -// if (!IsControlledFlight() && !(ground && fProbeHitGround && fAnimPosVel.fX == 0 && fAnimPosVel.fY == 0)) -// { -// hsVector3 curV; -// fPhysical->GetLinearVelocitySim(curV); -// fAnimPosVel.fZ = curV.fZ; -// -// // Prevents us from going airborn from running up bumps/inclines. -// if (IsOnGround() && fAnimPosVel.fZ > 0.f) -// fAnimPosVel.fZ = 0.f; -// -// // Unless we're on the ground and moving, or standing still with a probe hit, we use the sim's other axes too. -// if (!(IsOnGround() && (fProbeHitGround || fAnimPosVel.fX != 0 || fAnimPosVel.fY != 0))) -// { -// fAnimPosVel.fX = curV.fX; -// fAnimPosVel.fY = curV.fY; -// } -// } -// -// fPhysical->SetLinearVelocitySim(fAnimPosVel); -// fPhysical->SetSpin(fAnimAngVel + fTurnStr, hsVector3(0.0f, 0.0f, 1.0f)); -} -#endif - - -#if 0 - -///////////////////////////////////////////////////////////////////////// - -plSimDefs::ActionType plHorizontalFreezeAction::GetType() -{ - return plSimDefs::kHorizontalFreeze; -} - -void plHorizontalFreezeAction::apply(Havok::Subspace &s, Havok::hkTime time) -{ - double elapsed = time.asDouble() - getRefresh().asDouble(); - setRefresh(time); - - int numContacts = fPhysical->GetNumContacts(); - bool ground = false; - const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); - int i; - for(i = 0; i < numContacts; i++) - { - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - float dotUp = straightUp.dot(contact->m_normal); - if (dotUp > .5) - ground = true; - } - - hsVector3 vel; - fPhysical->GetLinearVelocitySim(vel); - vel.fX = 0.0; - vel.fY = 0.0; - if (ground) - vel.fZ = 0; - fPhysical->SetLinearVelocitySim(vel); - fPhysical->ClearContacts(); -} -#endif -plSwimmingController::plSwimmingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) -:plAnimatedController(rootObject,rootApp,controller) -{ - if (controller) - fSwimmingStrategy= new plSwimStrategy(controller); - else - fSwimmingStrategy = nil; -} -plSwimmingController::~plSwimmingController() -{ - delete fSwimmingStrategy; -} - -plRidingAnimatedPhysicalController::plRidingAnimatedPhysicalController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) -: plWalkingController(rootObject, rootApp, controller) -{ - if(controller) - fWalkingStrategy = new plRidingAnimatedPhysicalStrategy(controller); - else - fWalkingStrategy = nil; -} -plRidingAnimatedPhysicalController::~plRidingAnimatedPhysicalController() -{ - delete fWalkingStrategy; - fWalkingStrategy=nil; -} - - -////////////////////////////////////////////////////////////////////////// - - -/* -Purpose: - -ANGLE_RAD_2D returns the angle in radians swept out between two rays in 2D. - -Discussion: - -Except for the zero angle case, it should be true that - -ANGLE_RAD_2D(X1,Y1,X2,Y2,X3,Y3) -+ ANGLE_RAD_2D(X3,Y3,X2,Y2,X1,Y1) = 2 * PI - -Modified: - -19 April 1999 - -Author: - -John Burkardt - -Parameters: - -Input, float X1, Y1, X2, Y2, X3, Y3, define the rays -( X1-X2, Y1-Y2 ) and ( X3-X2, Y3-Y2 ) which in turn define the -angle, counterclockwise from ( X1-X2, Y1-Y2 ). - -Output, float ANGLE_RAD_2D, the angle swept out by the rays, measured -in radians. 0 <= ANGLE_DEG_2D < 2 PI. If either ray has zero length, -then ANGLE_RAD_2D is set to 0. -*/ - -static float AngleRad2d ( float x1, float y1, float x3, float y3 ) -{ - float value; - float x; - float y; - - x = ( x1 ) * ( x3 ) + ( y1 ) * ( y3 ); - y = ( x1 ) * ( y3 ) - ( y1 ) * ( x3 ); - - if ( x == 0.0 && y == 0.0 ) { - value = 0.0; - } - else - { - value = atan2 ( y, x ); - - if ( value < 0.0 ) - { - value = (float)(value + TWO_PI); - } - } - return value; -} - -static bool LinearVelocity(hsVector3 &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) -{ - bool result = false; - - hsPoint3 startPos(0.0f, 0.0f, 0.0f); // default position (at start of anim) - hsPoint3 prevPos = prevMat.GetTranslate(); // position previous frame - hsPoint3 nowPos = curMat.GetTranslate(); // position current frame - - hsVector3 prev2Now = (hsVector3)(nowPos - prevPos); // frame-to-frame delta - - if (fabs(prev2Now.fX) < 0.0001f && fabs(prev2Now.fY) < 0.0001f && fabs(prev2Now.fZ) < 0.0001f) - { - outputV.Set(0.f, 0.f, 0.f); - result = true; - } - else - { - hsVector3 start2Now = (hsVector3)(nowPos - startPos); // start-to-frame delta - - float prev2NowMagSqr = prev2Now.MagnitudeSquared(); - float start2NowMagSqr = start2Now.MagnitudeSquared(); - - float dot = prev2Now.InnerProduct(start2Now); - - // HANDLING ANIMATION WRAPPING: - // the vector from the animation origin to the current frame should point in roughly - // the same direction as the vector from the previous animation position to the - // current animation position. - // - // If they don't agree (dot < 0,) then we probably mpst wrapped around. - // The right answer would be to compare the current frame to the start of - // the anim loop, but it's cheaper to cheat and return false, - // telling the caller to use the previous frame's velocity. - if (dot > 0.0f) - { - prev2Now /= elapsed; - - float xfabs = fabs(prev2Now.fX); - float yfabs = fabs(prev2Now.fY); - float zfabs = fabs(prev2Now.fZ); - static const float maxVel = 20.0f; - bool valid = xfabs < maxVel && yfabs < maxVel && zfabs < maxVel; - - if (valid) - { - outputV = prev2Now; - result = true; - } - } - } - - return result; -} - -static void AngularVelocity(float &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) -{ - outputV = 0.f; - float appliedVelocity = 0.0f; - hsVector3 prevForward = GetYAxis(prevMat); - hsVector3 curForward = GetYAxis(curMat); - - float angleSincePrev = AngleRad2d(curForward.fX, curForward.fY, prevForward.fX, prevForward.fY); - bool sincePrevSign = angleSincePrev > 0.0f; - if (angleSincePrev > M_PI) - angleSincePrev = angleSincePrev - TWO_PI; - - const hsVector3 startForward = hsVector3(0, -1.0, 0); // the Y orientation of a "resting" armature.... - float angleSinceStart = AngleRad2d(curForward.fX, curForward.fY, startForward.fX, startForward.fY); - bool sinceStartSign = angleSinceStart > 0.0f; - if (angleSinceStart > M_PI) - angleSinceStart = angleSinceStart - TWO_PI; - - // HANDLING ANIMATION WRAPPING: - // under normal conditions, the angle from rest to the current frame will have the same - // sign as the angle from the previous frame to the current frame. - // if it does not, we have (most likely) wrapped the motivating animation from frame n back - // to frame zero, creating a large angle from the previous frame to the current one - if (sincePrevSign == sinceStartSign) - { - // signs are the same; didn't wrap; use the frame-to-frame angle difference - appliedVelocity = angleSincePrev / elapsed; // rotation / time - if (fabs(appliedVelocity) < 3) - { - outputV = appliedVelocity; - } - } -} diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h deleted file mode 100644 index 3462ca08..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h +++ /dev/null @@ -1,217 +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 PL_HK_CALLBACK_ACTION_H -#define PL_HK_CALLBACK_ACTION_H - -#include "hsGeometry3.h" -#include "hsMatrix44.h" -#include "hsTemplates.h" -#include "pnKeyedObject/plKey.h" -#include "plPhysical/plSimDefs.h" -#include "pnMessage/plMessage.h" -#include "plPhysicalControllerCore.h" -class plLOSHitMsg; -class plAGApplicator; -class plSceneObject; -class plPhysical; -class plAvatarController; -class plCoordinateInterface; -class plPhysicalControllerCore; -// Used by the other controllers to actually move the avatar. The actual -// implementation is in the physics system. -/*class plPhysicalController -{ -public: - // Implemented in the physics system. If you're linking this without that for - // some reason, just stub this function out. - // - // Pass in the key to the root sceneobject for the avatar - static plPhysicalController* Create(plKey ownerSO, float height, float width); - - virtual ~plPhysicalController() {} - - // A disabled avatar doesn't move or accumulate air time if he's off the ground. - virtual void Enable(bool enable) = 0; - virtual bool IsEnabled() const = 0; - - // Set the LOS DB this avatar will be in (only one) - virtual void SetLOSDB(plSimDefs::plLOSDB losDB) = 0; - - // Call this once per frame with the velocities of the avatar in avatar space. - virtual void SetVelocities(const hsVector3& linearVel, float angVel) = 0; - - // Gets the actual velocity we achieved in the last step (relative to our subworld) - virtual const hsVector3& GetLinearVelocity() const = 0; - virtual void ResetAchievedLinearVelocity() = 0; - - // Get and set the current subworld for the avatar. Use nil for the main world. - virtual plKey GetSubworld() const = 0; - virtual void SetSubworld(plKey world) = 0; - - // If IsOnGround returns false, GetAirTime will tell you how long the avatar - // has been airborne. Use ResetAirTime to reset the air time to zero, for - // cases like when the avatar spawns into a new age. - virtual bool IsOnGround() const = 0; - virtual bool IsOnFalseGround() const = 0; - virtual float GetAirTime() const = 0; - virtual void ResetAirTime() = 0; - - virtual plPhysical* GetPushingPhysical() const = 0; - virtual bool GetFacingPushingPhysical() const = 0; - - // A helper function to get the coordinate interface for the avatars current - // world. Handy if you need to convert points to and from that. This will - // return nil if the avatar is in the main world (ie, you don't need to do - // any translation). - virtual const plCoordinateInterface* GetSubworldCI() const = 0; - - // For the avatar SDL only - virtual void GetState(hsPoint3& pos, float& zRot) = 0; - virtual void SetState(const hsPoint3& pos, float zRot) = 0; - - // kinematic stuff .... should be just for when playing a behavior... - virtual void Kinematic(bool state) = 0; - virtual bool IsKinematic() = 0; - virtual void GetKinematicPosition(hsPoint3& pos) = 0; - - virtual const hsMatrix44& GetPrevSubworldW2L() = 0; - - //when seeking no longer want to interact with exclusion regions - virtual void SetSeek(bool seek)=0; - - -}; -*/ -class plAvatarController -{ -public: - virtual ~plAvatarController() {} -}; - -class plAnimatedController : public plAvatarController -{ -public: - plAnimatedController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - - virtual void RecalcVelocity(double timeNow, double timePrev, bool useAnim = true); - void SetTurnStrength(float val) { fTurnStr = val; } - float GetTurnStrength() { return fTurnStr; } - virtual void ActivateController()=0; -protected: - plSceneObject* fRootObject; - plPhysicalControllerCore* fController; - plAGApplicator* fRootApp; - float fAnimAngVel; - hsVector3 fAnimPosVel; - float fTurnStr; // Explicit turning, separate from animations -}; - -class plWalkingController : public plAnimatedController -{ -public: - plWalkingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - virtual ~plWalkingController(); - virtual void RecalcVelocity(double timeNow, double timePrev, bool useAnim = true); - - void Reset(bool newAge); - bool IsControlledFlight() const { return fControlledFlight != 0; } - bool IsOnGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnGround() : true; } - bool IsOnFalseGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnFalseGround() : true; } - bool HitGroundInThisAge() const { return fHitGroundInThisAge; } - bool EnableControlledFlight(bool status); - float GetAirTime() const { return fWalkingStrategy ? fWalkingStrategy->GetAirTime() : 0.f; } - void ResetAirTime() { if (fWalkingStrategy) fWalkingStrategy->ResetAirTime(); } - float GetForwardVelocity() const; - void ActivateController(); - // Check these after the avatar the avatar hits the ground for his total - // hangtime and impact velocity. - float GetImpactTime() const { return fImpactTime; } - const hsVector3& GetImpactVelocity() const { return fImpactVelocity; } - - plPhysical* GetPushingPhysical() const - { - if(fController)return fController->GetPushingPhysical(); - else return nil; - } - bool GetFacingPushingPhysical() const - { if(fController)return fController->GetFacingPushingPhysical(); - else return false; - } - - -protected: - bool fHitGroundInThisAge; - bool fWaitingForGround; // We've gone airborne. IsOnGround() returns false until we hit ground again. - float fControlledFlightTime; - int fControlledFlight; // Count of how many are currently forcing flight - plWalkingStrategy* fWalkingStrategy; - float fImpactTime; - hsVector3 fImpactVelocity; - bool fClearImpact; - bool fGroundLastFrame;//used for a test to pass the event of first getting air during a jump - static const float kControlledFlightThreshold; -}; -class plSwimmingController: public plAnimatedController -{ -public : - plSwimmingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - virtual ~plSwimmingController(); - void SetSurface(plSwimRegionInterface *region, float surfaceHeight){ - fSwimmingStrategy->SetSurface(region,surfaceHeight); - } - float GetBuoyancy() { return fSwimmingStrategy->GetBuoyancy(); } - bool IsOnGround() { return fSwimmingStrategy->IsOnGround(); } - bool HadContacts() { return fSwimmingStrategy->HadContacts();} - void Enable(bool en){if (fController) fController->Enable(en);} - plPhysicalControllerCore* GetController(){return fController;} - virtual void ActivateController(){fSwimmingStrategy->RefreshConnectionToControllerCore();} -protected: - plSwimStrategy* fSwimmingStrategy; - -}; -class plRidingAnimatedPhysicalController: public plWalkingController -{ -public: - plRidingAnimatedPhysicalController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - virtual ~plRidingAnimatedPhysicalController(); -}; -#endif // PL_HK_CALLBACK_ACTION_H diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp index f1ef12f5..a1fdf22d 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp @@ -39,7 +39,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvatar/plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "HeadSpin.h" @@ -129,7 +129,7 @@ bool plAvLadderMod::IIsReadyToClimb() if (armMod) { plAvBrainHuman* brain = plAvBrainHuman::ConvertNoRef(armMod->GetCurrentBrain()); - if (brain && brain->IsMovingForward() && brain->fCallbackAction->IsOnGround()) + if (brain && brain->IsMovingForward() && brain->fWalkingStrategy->IsOnGround()) movingForward = true; } diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp index 1207a180..60f2322e 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp @@ -54,7 +54,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAGAnim.h" #include "plArmatureMod.h" #include "plAvatarMgr.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" // other #include "plMessage/plAvatarMsg.h" @@ -463,10 +463,10 @@ bool plAvTaskSeek::IFinishPosition(hsPoint3 &newPosition, { // While warping, we might be hovering just above the ground. Don't want that to // trigger any falling behavior. - if(brain&&brain->fCallbackAction) + if(brain&&brain->fWalkingStrategy) { - brain->fCallbackAction->ResetAirTime(); + brain->fWalkingStrategy->ResetAirTime(); } // how far will we translate this frame? float thisDist = kFloatSpeed * elapsed; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp index 5d202273..ab3f9070 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp @@ -47,7 +47,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plAvBrainClimb.h" #include "plAvatar/plAvBrainDrive.h" #include "plAvatar/plAnimStage.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" #include "pnSceneObject/plSceneObject.h" #include "pnMessage/plSDLModifierMsg.h" #include "plSDL/plSDL.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp index b05d666c..a7f500c7 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp @@ -52,7 +52,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAGAnimInstance.h" #include "plAGModifier.h" #include "plMatrixChannel.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plAvatarMgr.h" // global @@ -671,7 +671,7 @@ bool plAvOneShotTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, dou if(fEnablePhysicsAtEnd) { #if 0//ndef PLASMA_EXTERNAL_RELEASE - if (!humanBrain || humanBrain->fCallbackAction->HitGroundInThisAge()) + if (!humanBrain || humanBrain->fWalkingStrategy->HitGroundInThisAge()) { // For some reason, calling CheckValidPosition at the beginning of // an age can cause detectors to incorrectly report collisions. So diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index 9ccb20a6..ed3b13d4 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -40,507 +40,601 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plPhysicalControllerCore.h" -#include "plMessage/plLOSHitMsg.h" + +#include "plArmatureMod.h" +#include "plSwimRegion.h" +#include "plMatrixChannel.h" #include "pnSceneObject/plCoordinateInterface.h" #include "plPhysical.h" #include "pnMessage/plCorrectionMsg.h" -#include "plSwimRegion.h" -#include "plArmatureMod.h" // for LOS enum type -#include "plMatrixChannel.h" -#include "hsTimer.h" -#include "plPhysX/plSimulationMgr.h" -#include "plPhysX/plPXPhysical.h" -#include "pnMessage/plSetNetGroupIDMsg.h" -#define kSWIMRADIUS 1.1f -#define kSWIMHEIGHT 2.8f -#define kGENERICCONTROLLERRADIUS 1.1f -#define kGENERICCONTROLLERHEIGHT 2.8f - -//#define kSWIMMINGCONTACTSLOPELIMIT (cosf(hsDegreesToRadians(80.f))) -const float plMovementStrategy::kAirTimeThreshold = .1f; // seconds + +// Gravity constants +#define kGravity -32.174f +#define kTerminalVelocity kGravity + +static inline hsVector3 GetYAxis(hsMatrix44 &mat) { return hsVector3(mat.fMap[1][0], mat.fMap[1][1], mat.fMap[1][2]); } +static float AngleRad2d(float x1, float y1, float x3, float y3); + bool CompareMatrices(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance); -bool operator<(const plControllerSweepRecord left, const plControllerSweepRecord right) + + +// plPhysicalControllerCore +plPhysicalControllerCore::plPhysicalControllerCore(plKey OwnerSceneObject, float height, float radius) + : fOwner(OwnerSceneObject), + fWorldKey(nil), + fHeight(height), + fRadius(radius), + fLOSDB(plSimDefs::kLOSDBNone), + fMovementStrategy(nil), + fSimLength(0.0f), + fLocalRotation(0.0f, 0.0f, 0.0f, 1.0f), + fLocalPosition(0.0f, 0.0f, 0.0f), + fLastLocalPosition(0.0f, 0.0f, 0.0f), + fLinearVelocity(0.0f, 0.0f, 0.0f), + fAchievedLinearVelocity(0.0f, 0.0f, 0.0f), + fPushingPhysical(nil), + fFacingPushingPhysical(false), + fSeeking(false), + fEnabled(false), + fEnableChanged(false) { - if(left.TimeHit < right.TimeHit) return true; - else return false; + fLastGlobalLoc.Reset(); + fPrevSubworldW2L.Reset(); } -plMovementStrategy::plMovementStrategy(plPhysicalControllerCore* core) + +const plCoordinateInterface* plPhysicalControllerCore::GetSubworldCI() { - this->fTimeInAir=0.0f; - fCore=core; - fOwner=core->GetOwner(); - this->fPreferedControllerHeight=kGENERICCONTROLLERHEIGHT; - this->fPreferedControllerWidth=kGENERICCONTROLLERRADIUS; + if (fWorldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); + if (so) + return so->GetCoordinateInterface(); + } + + return nil; } -void plMovementStrategy::IApplyKinematic() + +void plPhysicalControllerCore::IncrementAngle(float deltaAngle) { - // first apply sceneobject update to the kinematic + hsVector3 axis; + float angle; + + fLocalRotation.NormalizeIfNeeded(); + fLocalRotation.GetAngleAxis(&angle, &axis); + if (axis.fZ < 0) + angle = (2.0f * float(M_PI)) - angle; // axis is backwards, so reverse the angle too + + angle += deltaAngle; + + // make sure we wrap around + if (angle < 0.0f) + angle = (2.0f * float(M_PI)) + angle; // angle is -, so this works like a subtract + if (angle >= (2.0f * float(M_PI))) + angle = angle - (2.0f * float(M_PI)); + + // set the new angle + axis.Set(0.0f, 0.0f, 1.0f); + fLocalRotation.SetAngleAxis(angle, axis); +} + +void plPhysicalControllerCore::IApply(float delSecs) +{ + fSimLength = delSecs; + + // Match controller to owner if transform has changed since the last frame plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(fLastGlobalLoc, l2w, 0.0001f)) + SetGlobalLoc(l2w); + + if (fEnabled) { - // 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, fCore->GetLastGlobalLoc(), .0001f)) + // Convert velocity from avatar to world space + if (!fLinearVelocity.IsEmpty()) { - fCore->SetKinematicLoc(l2w); - //fCore->SetGlobalLoc(l2w); + fLinearVelocity = l2w * fLinearVelocity; + + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + fLinearVelocity = subworldCI->GetWorldToLocal() * fLinearVelocity; } + + fMovementStrategy->Apply(delSecs); } } -plPhysicalControllerCore::~plPhysicalControllerCore() -{ -} -void plPhysicalControllerCore::Apply(float delSecs) -{ - fSimLength=delSecs; - hsAssert(fMovementInterface, "plPhysicalControllerCore::Apply() missing a movement interface"); - if(fMovementInterface)fMovementInterface->Apply(delSecs); -} -void plPhysicalControllerCore::PostStep(float delSecs) +void plPhysicalControllerCore::IUpdate(int numSubSteps, float alpha) { - hsAssert(fMovementInterface, "plPhysicalControllerCore::PostStep() missing a movement interface"); - if(fMovementInterface)fMovementInterface->PostStep(delSecs); + if (fEnabled) + { + // Update local position and acheived velocity + fLastLocalPosition = fLocalPosition; + GetPositionSim(fLocalPosition); + hsVector3 displacement = (hsVector3)(fLocalPosition - fLastLocalPosition); + fAchievedLinearVelocity = displacement / fSimLength; + + displacement /= (float)numSubSteps; + fLastLocalPosition = fLocalPosition - displacement; + hsPoint3 interpLocalPos = fLastLocalPosition + (displacement * alpha); + + // Update global location + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&interpLocalPos); + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); + fLastGlobalLoc = subL2W * fLastGlobalLoc; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + } + + fMovementStrategy->Update(fSimLength); + ISendCorrectionMessages(true); + } + else + { + // Update global location if in a subworld + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + hsMatrix44 l2s = fPrevSubworldW2L * fLastGlobalLoc; + const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); + fLastGlobalLoc = subL2W * l2s; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + + ISendCorrectionMessages(); + } + } + + if (fEnableChanged) + IHandleEnableChanged(); } -void plPhysicalControllerCore::Update(float delSecs) +void plPhysicalControllerCore::IUpdateNonPhysical(float alpha) { - hsAssert(fMovementInterface, "plPhysicalControllerCore::Update() missing a movement interface"); - if(fMovementInterface)fMovementInterface->Update(delSecs); - + // Update global location if owner transform hasn't changed. + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (CompareMatrices(fLastGlobalLoc, l2w, 0.0001f)) + { + hsVector3 displacement = (hsVector3)(fLocalPosition - fLastLocalPosition); + hsPoint3 interpLocalPos = fLastLocalPosition + (displacement * alpha); + + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&interpLocalPos); + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); + fLastGlobalLoc = subL2W * fLastGlobalLoc; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + } + + ISendCorrectionMessages(); + } } -void plPhysicalControllerCore::SendCorrectionMessages() + +void plPhysicalControllerCore::ISendCorrectionMessages(bool dirtySynch) { - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - plCorrectionMsg* corrMsg = new plCorrectionMsg; + plCorrectionMsg* corrMsg = new plCorrectionMsg(); corrMsg->fLocalToWorld = fLastGlobalLoc; corrMsg->fLocalToWorld.GetInverse(&corrMsg->fWorldToLocal); - corrMsg->fDirtySynch = true; - // 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->fDirtySynch = dirtySynch; corrMsg->AddReceiver(fOwner); corrMsg->Send(); } -plPhysicalControllerCore::plPhysicalControllerCore(plKey OwnerSceneObject, float height, float radius) -:fMovementInterface(nil) -,fOwner(OwnerSceneObject) -,fHeight(height) -,fRadius(radius) -,fWorldKey(nil) -,fLinearVelocity(0.f,0.f,0.f) -,fAngularVelocity(0.f) -,fAchievedLinearVelocity(0.0f,0.0f,0.0f) -,fLocalPosition(0.0f,0.0f,0.0f) -,fLocalRotation(0.0f,0.0f,0.0f,1.0f) -,fSeeking(false) -,fEnabled(true) -,fEnableChanged(false) -,fLOSDB(plSimDefs::kLOSDBNone) -,fDisplacementThisStep(0.f,0.f,0.f) -,fSimLength(0.f) -,fKinematic(false) -,fKinematicEnableNextUpdate(false) -,fNeedsResize(false) -,fPushingPhysical(nil) + + +// Movement Strategy +plMovementStrategy::plMovementStrategy(plPhysicalControllerCore* controller) + : fController(controller) { } -void plPhysicalControllerCore::UpdateSubstepNonPhysical() -{ - // 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 =GetPrevSubworldW2L()* soL2W; - l2w = ciL2W * l2w; - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - ((plCoordinateInterface*)so->GetCoordinateInterface())->SetTransform(l2w, w2l); - ((plCoordinateInterface*)so->GetCoordinateInterface())->FlushTransform(); - SetGlobalLoc(l2w); - } +void plMovementStrategy::Reset(bool newAge) { fController->SetMovementStrategy(this); } -} -void plPhysicalControllerCore::CheckAndHandleAnyStateChanges() -{ - if (IsEnabledChanged())HandleEnableChanged(); - if (IsKinematicChanged())HandleKinematicChanged(); - if (IsKinematicEnableNextUpdate())HandleKinematicEnableNextUpdate(); -} -void plPhysicalControllerCore::MoveActorToSim() +// Animated Movement Strategy +plAnimatedMovementStrategy::plAnimatedMovementStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plMovementStrategy(controller), + fRootApp(rootApp), + fAnimLinearVel(0.0f, 0.0f, 0.0f), + fAnimAngularVel(0.0f), + fTurnStr(0.0f) { - // Get the current position of the physical - hsPoint3 curLocalPos; - hsPoint3 lastLocalPos; - GetPositionSim(curLocalPos); - MoveKinematicToController(curLocalPos); - lastLocalPos=GetLocalPosition(); - fDisplacementThisStep= hsVector3(curLocalPos - lastLocalPos); - fLocalPosition = curLocalPos; - if(fSimLength>0.0f) - fAchievedLinearVelocity=fDisplacementThisStep/fSimLength; - else fAchievedLinearVelocity.Set(0.0f,0.0f,0.0f); -} -void plPhysicalControllerCore::IncrementAngle(float deltaAngle) -{ - float angle; - hsVector3 axis; - fLocalRotation.NormalizeIfNeeded(); - fLocalRotation.GetAngleAxis(&angle, &axis); - // adjust it (quaternions are weird...) - if (axis.fZ < 0) - angle = (2*M_PI) - angle; // axis is backwards, so reverse the angle too - angle += deltaAngle; - // make sure we wrap around - if (angle < 0) - angle = (2*M_PI) + angle; // angle is -, so this works like a subtract - if (angle >= (2*M_PI)) - angle = angle - (2*M_PI); - // and set the new angle - fLocalRotation.SetAngleAxis(angle, hsVector3(0,0,1)); } -void plPhysicalControllerCore::UpdateWorldRelativePos() +void plAnimatedMovementStrategy::RecalcVelocity(double timeNow, float elapsed, bool useAnim) { + if (useAnim) + { + // while you may think it would be correct to cache this, what we're actually asking is "what would the animation's + // position be at the previous time given its *current* parameters (particularly blends)" + hsMatrix44 prevMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timeNow - elapsed, true); + hsMatrix44 curMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timeNow, true); - // Apply rotation and translation - fLocalRotation.MakeMatrix(&fLastGlobalLoc); - fLastGlobalLoc.SetTranslate(&fLocalPosition); - // Localize to global coords if in a subworld - const plCoordinateInterface* ci = GetSubworldCI(); - if (ci) + IRecalcLinearVelocity(elapsed, prevMat, curMat); + IRecalcAngularVelocity(elapsed, prevMat, curMat); + + // Update controller rotation + float zRot = fAnimAngularVel + fTurnStr; + if (hsABS(zRot) > 0.0001f) + fController->IncrementAngle(zRot * elapsed); + } + else { - const hsMatrix44& l2w = ci->GetLocalToWorld(); - fLastGlobalLoc = l2w * fLastGlobalLoc; + fAnimLinearVel.Set(0.0f, 0.0f, 0.0f); + fAnimAngularVel = 0.0f; } + + // Update controller velocity + fController->SetLinearVelocity(fAnimLinearVel); } -plPhysical* plPhysicalControllerCore::GetPushingPhysical() + +void plAnimatedMovementStrategy::IRecalcLinearVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) { - return fPushingPhysical; + hsPoint3 startPos(0.0f, 0.0f, 0.0f); // default position (at start of anim) + hsPoint3 prevPos = prevMat.GetTranslate(); // position previous frame + hsPoint3 nowPos = curMat.GetTranslate(); // position current frame + + hsVector3 prev2Now = (hsVector3)(nowPos - prevPos); // frame-to-frame delta + + if (fabs(prev2Now.fX) < 0.0001f && fabs(prev2Now.fY) < 0.0001f && fabs(prev2Now.fZ) < 0.0001f) + { + fAnimLinearVel.Set(0.f, 0.f, 0.f); + } + else + { + hsVector3 start2Now = (hsVector3)(nowPos - startPos); // start-to-frame delta + + float prev2NowMagSqr = prev2Now.MagnitudeSquared(); + float start2NowMagSqr = start2Now.MagnitudeSquared(); + + float dot = prev2Now.InnerProduct(start2Now); + + // HANDLING ANIMATION WRAPPING: + // the vector from the animation origin to the current frame should point in roughly + // the same direction as the vector from the previous animation position to the + // current animation position. + // + // If they don't agree (dot < 0,) then we probably mpst wrapped around. + // The right answer would be to compare the current frame to the start of + // the anim loop, but it's cheaper to cheat and use the previous frame's velocity. + if (dot > 0.0f) + { + prev2Now /= elapsed; + + float xfabs = fabs(prev2Now.fX); + float yfabs = fabs(prev2Now.fY); + float zfabs = fabs(prev2Now.fZ); + static const float maxVel = 20.0f; + bool valid = xfabs < maxVel && yfabs < maxVel && zfabs < maxVel; + + if (valid) + { + fAnimLinearVel = prev2Now; + } + } + } } -const hsVector3& plPhysicalControllerCore::GetLinearVelocity() + +void plAnimatedMovementStrategy::IRecalcAngularVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) { - return fLinearVelocity; + fAnimAngularVel = 0.0f; + float appliedVelocity = 0.0f; + hsVector3 prevForward = GetYAxis(prevMat); + hsVector3 curForward = GetYAxis(curMat); + + float angleSincePrev = AngleRad2d(curForward.fX, curForward.fY, prevForward.fX, prevForward.fY); + bool sincePrevSign = angleSincePrev > 0.0f; + if (angleSincePrev > float(M_PI)) + angleSincePrev = angleSincePrev - TWO_PI; + + const hsVector3 startForward = hsVector3(0.0f, -1.0f, 0.0f); // the Y orientation of a "resting" armature.... + float angleSinceStart = AngleRad2d(curForward.fX, curForward.fY, startForward.fX, startForward.fY); + bool sinceStartSign = angleSinceStart > 0.0f; + if (angleSinceStart > float(M_PI)) + angleSinceStart = angleSinceStart - TWO_PI; + + // HANDLING ANIMATION WRAPPING: + // under normal conditions, the angle from rest to the current frame will have the same + // sign as the angle from the previous frame to the current frame. + // if it does not, we have (most likely) wrapped the motivating animation from frame n back + // to frame zero, creating a large angle from the previous frame to the current one + if (sincePrevSign == sinceStartSign) + { + // signs are the same; didn't wrap; use the frame-to-frame angle difference + appliedVelocity = angleSincePrev / elapsed; // rotation / time + if (fabs(appliedVelocity) < 3) + { + fAnimAngularVel = appliedVelocity; + } + } } -bool plPhysicalControllerCore::GetFacingPushingPhysical() + + +// Walking Strategy +plWalkingStrategy::plWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plAnimatedMovementStrategy(rootApp, controller), + fSlidingNormals(), + fImpactVelocity(0.0f, 0.0f, 0.0f), + fImpactTime(0.0f), + fTimeInAir(0.0f), + fControlledFlightTime(0.0f), + fControlledFlight(0), + fGroundHit(false), + fFalseGround(false), + fHeadHit(false), + fClearImpact(false), + fHitGroundInThisAge(false) { - return fFacingPushingPhysical; } -/////////////////////////// -//Walking Strategy + void plWalkingStrategy::Apply(float delSecs) { - //Apply Should Only be Called from a PhysicalControllerCore - hsAssert(fCore,"No Core shouldn't be Applying"); - uint32_t collideFlags = - 1<IsSeeking()) + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); + + // Add in gravity if the avatar's z velocity isn't being set explicitly + if (hsABS(velocity.fZ) < 0.001f) { - collideFlags|=(1<GetLinearVelocity(); - hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); - hsPoint3 positionBegin; - fCore->GetPositionSim(positionBegin); - bool recovered=false; - if (fCore->IsKinematic()) + + // If we're airborne and the velocity isn't set, use the velocity from + // the last frame so we maintain momentum. + if (!IsOnGround() && velocity.fX == 0.0f && velocity.fY == 0.0f) { - 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, fCore->GetLastGlobalLoc(), .0001f)) - { - fCore->SetKinematicLoc(l2w); - fCore->SetGlobalLoc(l2w); - } - } - return; + velocity.fX = achievedVelocity.fX; + velocity.fY = achievedVelocity.fY; } - if (!fCore->IsEnabled()) - return; - - bool gotGroundHit = fGroundHit; - fGroundHit = false; - - fCore->SetPushingPhysical(nil); - fCore->SetFacingPushingPhysical( false); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) + if (!fGroundHit && fSlidingNormals.Count()) { - static const float kGravity = -32.f; - // 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, fCore->GetLastGlobalLoc(), .0001f)) - fCore->SetGlobalLoc(l2w); - - // Convert our avatar relative velocity to subworld relative - if (!LinearVelocity.IsEmpty()) - { - LinearVelocity = l2w * LinearVelocity; - const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); - if (subworldCI) - LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; - } - - // 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(LinearVelocity.fZ) < 0.001f) + // 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.0f, 0.0f, 0.0f); + for (int i = 0; i < fSlidingNormals.GetCount(); i++) { - // 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. - float prevZVel = AchievedLinearVelocity.fZ; - if (IsOnGround()) - prevZVel = hsMinimum(prevZVel, 0.f); - float 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. - float maxGrav = -.001f / delSecs; - if (grav > maxGrav) - grav = maxGrav; - LinearVelocity.fZ = prevZVel + grav; - } + offset += fSlidingNormals[i]; + hsVector3 velNorm = velocity; - // If we're airborne and the velocity isn't set, use the velocity from - // the last frame so we maintain momentum. - if (!IsOnGround() && LinearVelocity.fX == 0.f && LinearVelocity.fY == 0.f) - { - LinearVelocity.fX = AchievedLinearVelocity.fX; - LinearVelocity.fY = AchievedLinearVelocity.fY; - } + if (velNorm.MagnitudeSquared() > 0.0f) + velNorm.Normalize(); - //make terminal velocity equal to k. it is wrong but has been this way and - //don't want to break any puzzles. on top of that it will reduce tunneling behavior - if(LinearVelocity.fZSetLinearVelocity(LinearVelocity); - // Scale the velocity to our actual step size (by default it's feet/sec) - hsVector3 vel(LinearVelocity.fX * delSecs, LinearVelocity.fY * delSecs, LinearVelocity.fZ * delSecs); - unsigned int colFlags = 0; - fGroundHit = false; - fFalseGround = false; - fContactNormals.Swap(fPrevSlidingNormals); - fContactNormals.SetCount(0); - fCore->Move(vel, collideFlags, colFlags); - ICheckForFalseGround(); - //if(fReqMove2) fCore->Move2(vel); - /*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&kBottom ) &&(fGroundHit==false)) - { - fFalseGround=true; + velocity = velocity.Magnitude() * proj; + } } - - if(colFlags&kTop) + if (offset.MagnitudeSquared() > 0.0f) { - fHitHead=true; - //Did you hit your head on a dynamic? - //with Physx's wonderful controller hit report vs flags issues we need to actually sweep to see - std::multiset< plControllerSweepRecord > HitsDynamic; - uint32_t testFlag=1<GetPositionSim(startPos); - endPos= startPos + vel; - int NumObjsHit=fCore->SweepControllerPath(startPos, endPos, true, false, testFlag, HitsDynamic); - if(NumObjsHit>0) - { - for(std::multiset< plControllerSweepRecord >::iterator curObj= HitsDynamic.begin(); - curObj!=HitsDynamic.end(); curObj++) - { - - hsAssert(curObj->ObjHit,"We allegedly hit something, but there is no plasma physical associated with it"); - if(curObj->ObjHit) - {//really we shouldn't have to check hitObj should be nil only if we miss, or the physX object - //doesn't have a user data associated with this either way this just shouldn't happen - hsVector3 hitObjVel; - curObj->ObjHit->GetLinearVelocitySim(hitObjVel); - hsVector3 relativevel=LinearVelocity-hitObjVel; - curObj->ObjHit->SetHitForce(relativevel * 10.0f * (*curObj).ObjHit->GetMass(), (*curObj).locHit); - } - } - HitsDynamic.clear(); - } + // 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(); + velocity += offset * 5.0f; } } + + if (velocity.fZ < kTerminalVelocity) + velocity.fZ = kTerminalVelocity; + + // Convert to a displacement vector + hsVector3 displacement = velocity * delSecs; + + // Reset vars and move the controller + fController->SetPushingPhysical(nil); + fController->SetFacingPushingPhysical(false); + fGroundHit = fFalseGround = fHeadHit = false; + fSlidingNormals.SetCount(0); + + unsigned int collideResults = 0; + unsigned int collideFlags = 1<IsSeeking()) + collideFlags |= (1<Move(displacement, collideFlags, collideResults); + + if ((!fGroundHit) && (collideResults & kBottom)) + fFalseGround = true; + + if (collideResults & kTop) + fHeadHit = true; } -void plWalkingStrategy::ICheckForFalseGround() +void plWalkingStrategy::Update(float delSecs) { - 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 float threshold = hsDegreesToRadians(240.f); - int numContacts = fContactNormals.GetCount() + fPrevSlidingNormals.GetCount(); - if (numContacts >= 2) + if (fGroundHit || fFalseGround) + fTimeInAir = 0.0f; + else { - // 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 < fContactNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = atan2(fContactNormals[i].fY, fContactNormals[i].fX); - } - for (i = 0; i < fPrevSlidingNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = atan2(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]) - { - float 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) + fTimeInAir += delSecs; + if (fHeadHit) { - // We got to the end. Check the last with the first and make your decision. - if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * M_PI))) - fFalseGround = true; + // If we're airborne and hit our head, override achieved velocity to avoid being shoved sideways + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); + + achievedVelocity.fX = velocity.fX; + achievedVelocity.fY = velocity.fY; + if (achievedVelocity.fZ > 0.0f) + achievedVelocity.fZ = 0.0f; + + fController->OverrideAchievedLinearVelocity(achievedVelocity); } } + + if (!fHitGroundInThisAge && IsOnGround()) + fHitGroundInThisAge = true; + + if (fClearImpact) + { + fImpactTime = 0.0f; + fImpactVelocity.Set(0.0f, 0.0f, 0.0f); + } + + if (IsOnGround()) + fClearImpact = true; + else + { + fImpactTime = fTimeInAir; + fImpactVelocity = fController->GetAchievedLinearVelocity(); + // convert orientation from subworld to avatar-local coordinates + fImpactVelocity = (hsVector3)fController->GetLocalRotation().Rotate(&fImpactVelocity); + fClearImpact = false; + } + + if (fControlledFlight != 0) + { + if (IsOnGround()) + fControlledFlightTime = fTimeInAir; + + if (fControlledFlightTime > kControlledFlightThreshold) + EnableControlledFlight(false); + } } -void plWalkingStrategy::Update(float delSecs) + +void plWalkingStrategy::AddContactNormals(hsVector3& vec) { - //Update Should Only be Called from a PhysicalControllerCore - hsAssert(fCore,"Running Update: but have no Core"); - float AngularVelocity=fCore->GetAngularVelocity(); - hsVector3 LinearVelocity=fCore->GetLinearVelocity(); + float dot = vec * kAvatarUp; + if (dot >= 0.5f) + fGroundHit = true; + else + fSlidingNormals.Append(vec); +} - if (!fCore->IsEnabled() || fCore->IsKinematic()) +void plWalkingStrategy::Reset(bool newAge) +{ + plMovementStrategy::Reset(newAge); + if (newAge) { - fCore->UpdateSubstepNonPhysical(); - return; + fTimeInAir = 0.0f; + fClearImpact = true; + fHitGroundInThisAge = false; + fSlidingNormals.SetCount(0); } - fCore->CheckAndHandleAnyStateChanges(); - if (!fGroundHit && !fFalseGround) - fTimeInAir += delSecs; - else - fTimeInAir = 0.f; - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) +} + +bool plWalkingStrategy::EnableControlledFlight(bool status) +{ + if (status) { - fCore->MoveActorToSim(); - if (AngularVelocity != 0.f) - { - float deltaAngle=AngularVelocity*delSecs; - fCore->IncrementAngle( deltaAngle); - } - // 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. - fCore->UpdateWorldRelativePos(); - fCore->SendCorrectionMessages(); - bool headhit=fHitHead; - fHitHead=false; - hsVector3 AchievedLinearVelocity; - AchievedLinearVelocity = fCore->DisplacementLastStep(); - AchievedLinearVelocity=AchievedLinearVelocity/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 - */ - 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 - float headFriction=0.0f; - AchievedLinearVelocity.fX=(1.0f-headFriction)*LinearVelocity.fX; - AchievedLinearVelocity.fY=(1.0f-headFriction)*LinearVelocity.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(AchievedLinearVelocity.fZ>0.0f) - { - AchievedLinearVelocity.fZ=0.0f; - } - fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - } - fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - // Apply angular velocity + if (fControlledFlight == 0) + fControlledFlightTime = 0.0f; + + ++fControlledFlight; } + else + fControlledFlight = max(--fControlledFlight, 0); - LinearVelocity.Set(0.f, 0.f, 0.f); - AngularVelocity = 0.f; - fCore->SetVelocities(LinearVelocity,AngularVelocity); + return status; +} + +plPhysical* plWalkingStrategy::GetPushingPhysical() const { return fController->GetPushingPhysical(); } +bool plWalkingStrategy::GetFacingPushingPhysical() const { return fController->GetFacingPushingPhysical(); } + +const float plWalkingStrategy::kAirTimeThreshold = 0.1f; +const float plWalkingStrategy::kControlledFlightThreshold = 1.0f; + +// Swim Strategy +plSwimStrategy::plSwimStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plAnimatedMovementStrategy(rootApp, controller), + fBuoyancy(0.0f), + fSurfaceHeight(0.0f), + fCurrentRegion(nil), + fOnGround(false), + fHadContacts(false) +{ } +void plSwimStrategy::Apply(float delSecs) +{ + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); + + IAdjustBuoyancy(); + + //trying to dampen the oscillations + float retardent = 0.0f; + static float finalBobSpeed = 0.5f; + if ((achievedVelocity.fZ > finalBobSpeed) || (achievedVelocity.fZ < -finalBobSpeed)) + retardent = achievedVelocity.fZ * -0.90f; + + float zacc = (1.0f - fBuoyancy) * kGravity + retardent; + velocity.fZ += (zacc * delSecs); + + velocity.fZ += achievedVelocity.fZ; + + // Water Current + if (fCurrentRegion != nil) + { + float angCurrent = 0.0f; + hsVector3 linCurrent(0.0f, 0.0f, 0.0f); + fCurrentRegion->GetCurrent(fController, linCurrent, angCurrent, delSecs); + + if (hsABS(angCurrent) > 0.0001f) + fController->IncrementAngle(angCurrent * delSecs); + + velocity += linCurrent; + + if (velocity.fZ > fCurrentRegion->fMaxUpwardVel) + velocity.fZ = fCurrentRegion->fMaxUpwardVel; + } + + if (velocity.fZ < kTerminalVelocity) + velocity.fZ = kTerminalVelocity; + + // Convert to displacement vector + hsVector3 displacement = velocity * delSecs; + + // Reset vars and move controller // + fController->SetPushingPhysical(nil); + fController->SetFacingPushingPhysical(false); + fHadContacts = fOnGround = false; + + unsigned int collideResults = 0; + unsigned int collideFlags = 1<IsSeeking()) + collideFlags |= (1<Move(displacement, collideFlags, collideResults); + + if ((collideResults & kBottom) || (collideResults & kSides)) + fHadContacts = true; +} + +void plSwimStrategy::AddContactNormals(hsVector3& vec) +{ float dot = vec * kAvatarUp; - if ( dot >= kSLOPELIMIT ) fGroundHit=true; - else plMovementStrategySimulationInterface::IAddContactNormals(vec); + if (dot >= kSlopeLimit) + fOnGround = true; } -//swimming strategy -plSwimStrategy::plSwimStrategy(plPhysicalControllerCore* core) - :plMovementStrategy(core) - ,fOnGround(false) - ,fHadContacts(false) - ,fBuoyancy(0.f) - ,fSurfaceHeight(0.0f) - ,fCurrentRegion(nil) +void plSwimStrategy::SetSurface(plSwimRegionInterface *region, float surfaceHeight) { - fPreferedControllerHeight=kSWIMHEIGHT; - fPreferedControllerWidth=kSWIMRADIUS; - fCore->SetMovementSimulationInterface(this); + fCurrentRegion = region; + fSurfaceHeight = surfaceHeight; } void plSwimStrategy::IAdjustBuoyancy() { @@ -558,383 +652,148 @@ void plSwimStrategy::IAdjustBuoyancy() return; } - hsMatrix44 l2w, w2l; hsPoint3 posSim; - fCore->GetPositionSim(posSim); - float depth = fSurfaceHeight - posSim.fZ; - //this isn't a smooth transition but hopefully it won't be too obvious - if(depth<=0.0)//all the away above water + fController->GetPositionSim(posSim); + float depth = fSurfaceHeight - posSim.fZ; + + // this isn't a smooth transition but hopefully it won't be too obvious + if (depth <= 0.0) //all the away above water fBuoyancy = 0.f; // Same as being above ground. Plain old gravity. - else if(depth >= 5.0f) fBuoyancy=3.0f;//completely Submereged - else fBuoyancy =(depth/surfaceDepth ); - + else if (depth >= 5.0f) + fBuoyancy = 3.0f; //completely Submereged + else + fBuoyancy = depth / surfaceDepth; } -void plSwimStrategy::Apply(float delSecs) -{ - hsAssert(fCore,"PlSwimStrategy::Apply No Core shouldn't be Applying"); - uint32_t collideFlags = - 1<IsSeeking()) - { - collideFlags|=(1<GetLinearVelocity(); - hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); - if (fCore->IsKinematic()) - { - 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, fCore->GetLastGlobalLoc(), .0001f)) - { - fCore->SetKinematicLoc(l2w); - fCore->SetGlobalLoc(l2w); - } - } - return; - - } - if (!fCore->IsEnabled()) - return; - - fCore->SetPushingPhysical(nil); - fCore->SetFacingPushingPhysical( false); - fHadContacts=false; - fOnGround=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, fCore->GetLastGlobalLoc(), .0001f)) - fCore->SetGlobalLoc(l2w); - - // Convert our avatar relative velocity to subworld relative - if (!LinearVelocity.IsEmpty()) - { - LinearVelocity = l2w * LinearVelocity; - const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); - if (subworldCI) - LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; - } - IAdjustBuoyancy(); - float zacc; - float retardent=0.0f; - static float FinalBobSpeed=0.5f; - //trying to dampen the oscillations - if((AchievedLinearVelocity.fZ>FinalBobSpeed)||(AchievedLinearVelocity.fZ<-FinalBobSpeed)) - retardent=AchievedLinearVelocity.fZ *-.90f; - zacc=(1-fBuoyancy)*-32.f + retardent; - - hsVector3 linCurrent(0.0f,0.0f,0.0f); - float angCurrent = 0.f; - if (fCurrentRegion != nil) - { - - fCurrentRegion->GetCurrent(fCore, linCurrent, angCurrent, delSecs); - //fAngularVelocity+= angCurrent; - } - hsVector3 vel(LinearVelocity.fX , LinearVelocity.fY , AchievedLinearVelocity.fZ+ LinearVelocity.fZ ); - vel.fZ= vel.fZ + zacc*delSecs; - if(fCurrentRegion!=nil){ - if (vel.fZ > fCurrentRegion->fMaxUpwardVel) - { - vel.fZ = fCurrentRegion->fMaxUpwardVel; - } - vel+= linCurrent; - } - static const float kGravity = -32.f; - if(vel.fZMove(displacement,collideFlags,colFlags); - if((colFlags&kBottom)||(colFlags&kSides))fHadContacts=true; - float angvel=fCore->GetAngularVelocity(); - fCore->SetAngularVelocity(angvel +angCurrent); - } + +// Dynamic Walking Strategy +plDynamicWalkingStrategy::plDynamicWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plWalkingStrategy(rootApp, controller) +{ } -void plSwimStrategy::Update(float delSecs) + +void plDynamicWalkingStrategy::Apply(float delSecs) { - hsAssert(fCore,"Running Update: but have no Core"); - float AngularVelocity=fCore->GetAngularVelocity(); - hsVector3 LinearVelocity=fCore->GetLinearVelocity(); - if (!fCore->IsEnabled() || fCore->IsKinematic()) - { - fCore->UpdateSubstepNonPhysical(); - return; - } - fCore->CheckAndHandleAnyStateChanges(); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) - { - fCore->MoveActorToSim(); + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); - if (AngularVelocity != 0.f) - { - float deltaAngle=AngularVelocity*delSecs; - fCore->IncrementAngle( deltaAngle); - } - fCore->UpdateWorldRelativePos(); - fCore->SendCorrectionMessages(); - } - LinearVelocity.Set(0.f, 0.f, 0.f); - AngularVelocity = 0.f; - fCore->SetVelocities(LinearVelocity,AngularVelocity); -} -void plSwimStrategy::IAddContactNormals(hsVector3& vec) -{ - //TODO: ADD in functionality to Adjust walkable slope for controller, also apply that in here - float dot = vec * kAvatarUp; - if ( dot >= kSLOPELIMIT ) + // Add in gravity if the avatar's z velocity isn't being set explicitly + if (hsABS(velocity.fZ) < 0.001f) { - fOnGround=true; - // fHadContacts=true; + // 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. + float prevZVel = achievedVelocity.fZ; + if (IsOnGround()) + prevZVel = hsMinimum(prevZVel, 0.f); + + velocity.fZ = prevZVel + (kGravity * delSecs); } - else plMovementStrategySimulationInterface::IAddContactNormals(vec); -} -void plSwimStrategy::SetSurface(plSwimRegionInterface *region, float surfaceHeight) -{ - fCurrentRegion=region; - fSurfaceHeight=surfaceHeight; + + if (velocity.fZ < kTerminalVelocity) + velocity.fZ = kTerminalVelocity; + + fController->SetPushingPhysical(nil); + fController->SetFacingPushingPhysical(false); + fGroundHit = fFalseGround = false; + + float groundZVelocity; + if (ICheckForGround(groundZVelocity)) + velocity.fZ += groundZVelocity; + + fController->SetLinearVelocitySim(velocity); } -void plRidingAnimatedPhysicalStrategy::Apply(float delSecs) + +bool plDynamicWalkingStrategy::ICheckForGround(float& zVelocity) { - hsVector3 LinearVelocity=fCore->GetLinearVelocity(); - hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); - if (fCore->IsKinematic()) - { - //want to make sure nothing funky happens in the sim - IApplyKinematic(); - return; - } - if (!fCore->IsEnabled()) - return; + std::vector groundHits; + uint32_t collideFlags = 1<GetPositionSim(startPos); + hsPoint3 endPos = startPos; - - fCore->SetPushingPhysical(nil); - fCore->SetFacingPushingPhysical( false); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - hsPoint3 startPos, desiredDestination, endPos; - fCore->GetPositionSim(startPos); - uint32_t collideFlags = - 1< GroundHitRecords; - int possiblePlatformCount =fCore->SweepControllerPath(startPos, startPos + hsPoint3(0.f,0.f, -0.002f), true, true, collideFlags, GroundHitRecords); - float maxPlatformVel = - FLT_MAX; - int platformCount=0; - fGroundHit = false; - if(possiblePlatformCount) + // Set sweep length + startPos.fZ += 0.05f; + endPos.fZ -= 0.05f; + + int possiblePlatformCount = fController->SweepControllerPath(startPos, endPos, true, true, collideFlags, groundHits); + if (possiblePlatformCount) { - - std::multiset::iterator curRecord; + zVelocity = -FLT_MAX; - for(curRecord = GroundHitRecords.begin(); curRecord != GroundHitRecords.end(); curRecord++) + std::vector::iterator curRecord; + for (curRecord = groundHits.begin(); curRecord != groundHits.end(); ++curRecord) { - bool groundlike=false; - if((curRecord->locHit.fZ - startPos.fZ)<= .2) groundlike= true; - if(groundlike) + if (curRecord->ObjHit != nil) { - if(curRecord->ObjHit !=nil) - { - hsVector3 vel; - curRecord->ObjHit->GetLinearVelocitySim(vel); - if(vel.fZ > maxPlatformVel) - { - maxPlatformVel= vel.fZ; - } - } - platformCount ++; + hsVector3 objVelocity; + curRecord->ObjHit->GetLinearVelocitySim(objVelocity); + if (objVelocity.fZ > zVelocity) + zVelocity = objVelocity.fZ; + fGroundHit = true; } } } - - - - bool gotGroundHit = fGroundHit; - 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, fCore->GetLastGlobalLoc(), .0001f)) - fCore->SetGlobalLoc(l2w); - - // Convert our avatar relative velocity to subworld relative - if (!LinearVelocity.IsEmpty()) - { - LinearVelocity = l2w * LinearVelocity; - const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); - if (subworldCI) - LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; - } - - if(!IsOnGround()) - { - if(!fNeedVelocityOverride) - { - LinearVelocity.fZ= AchievedLinearVelocity.fZ; - } - else - { - LinearVelocity = fOverrideVelocity; - } - } - if(fStartJump) - { - LinearVelocity.fZ =12.0f; - } - if(platformCount) - { - LinearVelocity.fZ = LinearVelocity.fZ + maxPlatformVel; - } - - //probably neeed to do something with contact normals in here - //for false ground stuff - - fFalseGround = false; - hsVector3 testLength = LinearVelocity * delSecs + hsVector3(0.0, 0.0, -0.00f); - // - hsPoint3 desiredDestination= startPos + testLength; - if(!IsOnGround()) - { - if(ICheckMove(startPos, desiredDestination)) - {//we can get there soley by the LinearVelocity - - fNeedVelocityOverride =false; - } - else - { - - fNeedVelocityOverride =true; - fOverrideVelocity = LinearVelocity; - fOverrideVelocity.fZ -= delSecs * 32.f; - } - } - else - { - fNeedVelocityOverride =false; - } - fCore->SetLinearVelocity(LinearVelocity); - - } + return fGroundHit; } -bool plRidingAnimatedPhysicalStrategy::ICheckMove(const hsPoint3& startPos, const hsPoint3& desiredPos) + + +////////////////////////////////////////////////////////////////////////// + +/* +Purpose: + +ANGLE_RAD_2D returns the angle in radians swept out between two rays in 2D. + +Discussion: + +Except for the zero angle case, it should be true that + +ANGLE_RAD_2D(X1,Y1,X2,Y2,X3,Y3) ++ ANGLE_RAD_2D(X3,Y3,X2,Y2,X1,Y1) = 2 * PI + +Modified: + +19 April 1999 + +Author: + +John Burkardt + +Parameters: + +Input, float X1, Y1, X2, Y2, X3, Y3, define the rays +( X1-X2, Y1-Y2 ) and ( X3-X2, Y3-Y2 ) which in turn define the +angle, counterclockwise from ( X1-X2, Y1-Y2 ). + +Output, float ANGLE_RAD_2D, the angle swept out by the rays, measured +in radians. 0 <= ANGLE_DEG_2D < 2 PI. If either ray has zero length, +then ANGLE_RAD_2D is set to 0. +*/ + +static float AngleRad2d( float x1, float y1, float x3, float y3 ) { - //returns false if it believes the end result can't be obtained by pure application of velocity (collides into somthing that it can't climb up) - //used as a way to check if it needs to hack getting there like in jumping - - uint32_t collideFlags = - 1<IsSeeking()) - { - collideFlags|=(1< DynamicHits; - int NumberOfHits=fCore->SweepControllerPath(startPos, desiredPos, true, true, collideFlags, DynamicHits); - - hsPoint3 stepFromPoint; - hsVector3 movementdir(&startPos, &desiredPos); - movementdir.Normalize(); - if(NumberOfHits) - { - hsPoint3 initBottomPos; - fCore->GetPositionSim(initBottomPos); - std::multiset< plControllerSweepRecord >::iterator cur; - hsVector3 testLength(desiredPos - startPos); - bool freeMove=true; - for(cur = DynamicHits.begin(); cur != DynamicHits.end(); cur++) - { - if(movementdir.InnerProduct(cur->Norm)>0.01f) - { - hsVector3 topOfBottomHemAtTimeT=hsVector3(initBottomPos + testLength * cur->TimeHit ); - topOfBottomHemAtTimeT.fZ = topOfBottomHemAtTimeT.fZ + fCore->GetControllerWidth(); - if(cur->locHit.fZ <= (topOfBottomHemAtTimeT.fZ -.5f)) - { - hitBottomOfCapsule=true; - hsVector3 norm= hsVector3(-1*(cur->locHit-topOfBottomHemAtTimeT)); - norm.Normalize(); - IAddContactNormals(norm); - } - else - { - return false; - } - } + float value; + float x; + float y; - } - return true; + x = ( x1 ) * ( x3 ) + ( y1 ) * ( y3 ); + y = ( x1 ) * ( y3 ) - ( y1 ) * ( x3 ); + + if ( x == 0.0 && y == 0.0 ) { + value = 0.0; } else { - return true; - } - -} -void plRidingAnimatedPhysicalStrategy::Update(float delSecs) -{ - if (!fCore->IsEnabled() || fCore->IsKinematic()) - { - fCore->UpdateSubstepNonPhysical(); - return; - } - fCore->CheckAndHandleAnyStateChanges(); -} -void plRidingAnimatedPhysicalStrategy::PostStep(float delSecs) -{ - if(!(!fCore->IsEnabled() || fCore->IsKinematic())) - { - if (!fGroundHit && !fFalseGround) - fTimeInAir += delSecs; - else - fTimeInAir = 0.f; - hsVector3 AchievedLinearVelocity, LinearVelocity; - AchievedLinearVelocity = fCore->GetLinearVelocity(); - float AngularVelocity=fCore->GetAngularVelocity(); - fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) + value = atan2 ( y, x ); + + if ( value < 0.0 ) { - fCore->UpdateControllerAndPhysicalRep(); - if (AngularVelocity != 0.f) - { - float deltaAngle=AngularVelocity*delSecs; - fCore->IncrementAngle( deltaAngle); - } - fCore->UpdateWorldRelativePos(); - fCore->SendCorrectionMessages(); + value = (float)(value + TWO_PI); } - LinearVelocity.Set(0.f, 0.f, 0.f); - AngularVelocity = 0.f; - fCore->SetVelocities(LinearVelocity, AngularVelocity); } - fStartJump = false; + return value; } + diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h index bb2c13cd..808a6702 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h @@ -41,310 +41,284 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #ifndef PLPHYSICALCONTROLLERCORE_H #define PLPHYSICALCONTROLLERCORE_H + #include "hsGeometry3.h" #include "hsMatrix44.h" +#include "hsQuat.h" #include "hsTemplates.h" #include "pnKeyedObject/plKey.h" #include "plPhysical/plSimDefs.h" -#include "pnMessage/plMessage.h" - -#include "hsQuat.h" -#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 -#define kSLOPELIMIT (cosf(hsDegreesToRadians(45.f))) +#include class plCoordinateInterface; class plPhysical; -class plPXPhysical; +class plMovementStrategy; +class plAGApplicator; class plSwimRegionInterface; -//Replacement for for plPhysicalController stripped out some walk specific code -//plPhysicalControllerCore needs to have movement strategies registered to it these will then -//be called by the controller during the simulation steps. The Strategies need to at least have an -// Apply and Update definition. Everything else should be movement specific. I hope to come back and -//and refactor when I have time this in the future. -enum plControllerCollisionFlags -{ - kSides=1, - kTop= (1<<1), - kBottom=(1<<2), -}; -class plMovementStrategySimulationInterface -{ -public: +#define kSlopeLimit (cosf(hsDegreesToRadians(55.f))) - virtual void Apply(float delSecs)=0; - virtual void Update(float delSecs)=0; - //most strategies don't require this. Only the ones that require behavior like a physical or need - //something after the sim step. this used to be taken care of by Update, but this was moved to take care of - //some of the frame lag - virtual void PostStep(float delSecs){}; - virtual void IAddContactNormals(hsVector3& vec){fContactNormals.Append(vec);} - virtual void AddOnTopOfObject(plPhysical* phys){ fOnTopOf.Append(phys);} - virtual void LeaveAge() - { - fContactNormals.SetCount(0); - fOnTopOf.SetCount(0); - } -protected: - hsTArray fContactNormals; - hsTArray fOnTopOf; +enum plControllerCollisionFlags +{ + kSides = 1, + kTop = (1 << 1), + kBottom = (1 << 2) }; -class plControllerSweepRecord +struct plControllerSweepRecord { -public: plPhysical *ObjHit; - hsPoint3 locHit;//World space - float TimeHit;//Normalized between 0 and 1 - hsVector3 Norm; + hsPoint3 Point; + hsVector3 Normal; }; -bool operator<(const plControllerSweepRecord left, const plControllerSweepRecord right); + class plPhysicalControllerCore { public: - virtual ~plPhysicalControllerCore(); - virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults)=0; - virtual void SetMovementSimulationInterface(plMovementStrategySimulationInterface* strat){fMovementInterface=strat;} - virtual void Apply(float delSecs); - virtual void Update(float delSecs); - virtual void PostStep(float delSecs); - // A disabled avatar doesn't move or accumulate air time if he's off the ground. + plPhysicalControllerCore(plKey ownerSceneObject, float height, float radius); + virtual ~plPhysicalControllerCore() { } + + // An ArmatureMod has its own idea about when physics should be enabled/disabled. + // Use plArmatureModBase::EnablePhysics() instead. virtual void Enable(bool enable) = 0; - virtual bool IsEnabled() {return fEnabled;} - virtual plKey GetSubworld() {return fWorldKey;} + virtual bool IsEnabled() { return fEnabled; } + + // Subworld + virtual plKey GetSubworld() { return fWorldKey; } virtual void SetSubworld(plKey world) = 0; - virtual const plCoordinateInterface* GetSubworldCI() const = 0; + virtual const plCoordinateInterface* GetSubworldCI(); + // For the avatar SDL only virtual void GetState(hsPoint3& pos, float& zRot) = 0; virtual void SetState(const hsPoint3& pos, float zRot) = 0; - // kinematic stuff .... should be just for when playing a behavior... - virtual void Kinematic(bool state) = 0; - virtual bool IsKinematic() = 0; - virtual void GetKinematicPosition(hsPoint3& pos) = 0; - virtual const hsMatrix44& GetPrevSubworldW2L() = 0; - //when seeking no longer want to interact with exclusion regions - virtual void SetSeek(bool seek){fSeeking=seek;} - virtual bool IsSeeking(){return fSeeking;} - static plPhysicalControllerCore* Create(plKey ownerSO, float height, float radius); - virtual plMovementStrategySimulationInterface* GetMovementInterface(){return fMovementInterface;} - plPhysicalControllerCore(plKey ownerSceneObject, float height, float radius); - virtual plKey GetOwner(){return fOwner;}; - // Set the LOS DB this avatar will be in (only one) - virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } ; - virtual plSimDefs::plLOSDB GetLOSDB() {return fLOSDB ; } - virtual const hsMatrix44& GetLastGlobalLoc()=0; - virtual void SetKinematicLoc(const hsMatrix44& l2w)=0; - virtual void SetGlobalLoc(const hsMatrix44& l2w)=0; - virtual bool IsEnabledChanged(){return fEnableChanged;} - virtual void HandleEnableChanged()=0; - virtual bool IsKinematicChanged(){return fKinematicChanged;} - virtual void GetPositionSim(hsPoint3& pos)=0; - virtual void HandleKinematicChanged()=0; - virtual bool IsKinematicEnableNextUpdate(){return fKinematicEnableNextUpdate;} - virtual void HandleKinematicEnableNextUpdate()=0; - virtual void MoveKinematicToController(hsPoint3& pos)=0; - virtual void UpdateControllerAndPhysicalRep()=0; - virtual void CheckAndHandleAnyStateChanges(); - virtual void UpdateSubstepNonPhysical(); - virtual const hsPoint3& GetLocalPosition()=0; - const hsQuat& GetLocalRotation() { return fLocalRotation; } - virtual void MoveActorToSim(); - - virtual void OverrideAchievedVelocity(hsVector3 newAchievedVel) - {//because of things like superjumps this is needed I'd rather not, but can't help it - fAchievedLinearVelocity=newAchievedVel; - } - //any clean up for the controller should go here - virtual void LeaveAge()=0; - hsVector3 DisplacementLastStep(){return fDisplacementThisStep;} - hsVector3 MeanVelocityForLastStep() - { - hsVector3 vel=fDisplacementThisStep; - return vel/fSimLength; - } - void SendCorrectionMessages(); + + // The LOS DB this avatar is in (only one) + virtual plSimDefs::plLOSDB GetLOSDB() { return fLOSDB; } + virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } + + // Movement strategy + virtual void SetMovementStrategy(plMovementStrategy* strategy) = 0; + + // Global location + virtual const hsMatrix44& GetLastGlobalLoc() { return fLastGlobalLoc; } + virtual void SetGlobalLoc(const hsMatrix44& l2w) = 0; + + // Local sim position + virtual void GetPositionSim(hsPoint3& pos) = 0; + + // Move kinematic controller + virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) = 0; + + // Set linear velocity on dynamic controller + virtual void SetLinearVelocitySim(const hsVector3& linearVel) = 0; + + // 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) = 0; + + // any clean up for the controller should go here + virtual void LeaveAge() = 0; + + // Local rotation + const hsQuat& GetLocalRotation() const { return fLocalRotation; } void IncrementAngle(float deltaAngle); - void UpdateWorldRelativePos(); - virtual void SetLinearVelocity(const hsVector3& linearVel){fLinearVelocity=linearVel;} - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void SetAngularVelocity(const float angvel){ fAngularVelocity=angvel;} - virtual void SetVelocities(const hsVector3& linearVel, float angVel) - { - fLinearVelocity=linearVel; - fAngularVelocity=angVel; - } - - virtual const hsVector3& GetLinearVelocity() ; - virtual float GetAngularVelocity(){return fAngularVelocity;} - virtual const hsVector3& GetAchievedLinearVelocity()const {return fAchievedLinearVelocity;} - plPhysical* GetPushingPhysical(); - bool GetFacingPushingPhysical(); - virtual void SetPushingPhysical(plPhysical* pl){fPushingPhysical=pl;} - virtual void SetFacingPushingPhysical(bool ans){fFacingPushingPhysical=ans;} - //To be Used during runtime conversions, say to switch a tall controller to a ball for swimming - virtual void SetControllerDimensions(float radius, float height)=0; - virtual float GetControllerWidth(){return fRadius;} - virtual float GetControllerHeight(){return fHeight;} - virtual void ResetAchievedLinearVelocity() - { - fAchievedLinearVelocity.Set(0.f,0.f,0.f); - } - virtual int SweepControllerPath(const hsPoint3& startPos,const hsPoint3& endPos, bool vsDynamics, bool vsStatics, uint32_t& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut)=0; - //this should only be used to force a move it could place your head into a wall and that would be good - virtual float GetHeight() {return fHeight;} - virtual float GetRadius() {return fRadius;} - //Wether the avatar thing has mass and forces things down or not, and changes the way things move - //This is an attempt fix things like riding on an animated physical - virtual void BehaveLikeAnimatedPhysical(bool actLikeAnAnimatedPhys)=0; - virtual bool BehavingLikeAnAnimatedPhysical()=0; + + // Linear velocity + void SetLinearVelocity(const hsVector3& linearVel) { fLinearVelocity = linearVel; } + const hsVector3& GetLinearVelocity() const { return fLinearVelocity; } + + // Acheived linear velocity + const hsVector3& GetAchievedLinearVelocity() const { return fAchievedLinearVelocity; } + void OverrideAchievedLinearVelocity(const hsVector3& linearVel) { fAchievedLinearVelocity = linearVel; } + void ResetAchievedLinearVelocity() { fAchievedLinearVelocity.Set(0.f, 0.f, 0.f); } + + // SceneObject + plKey GetOwner() { return fOwner; } + + // When seeking no longer want to interact with exclude regions + void SetSeek(bool seek) { fSeeking = seek; } + bool IsSeeking() const { return fSeeking; } + + // Pushing physical + plPhysical* GetPushingPhysical() const { return fPushingPhysical; } + void SetPushingPhysical(plPhysical* phys) { fPushingPhysical = phys; } + bool GetFacingPushingPhysical() const { return fFacingPushingPhysical; } + void SetFacingPushingPhysical(bool facing) { fFacingPushingPhysical = facing; } + + // Controller dimensions + float GetRadius() const { return fRadius; } + float GetHeight() const { return fHeight; } + + // Create a new controller instance - Implemented in the physics system + static plPhysicalControllerCore* Create(plKey ownerSO, float height, float radius); + protected: - + virtual void IHandleEnableChanged() = 0; + + void IApply(float delSecs); + void IUpdate(int numSubSteps, float alpha); + void IUpdateNonPhysical(float alpha); + + void ISendCorrectionMessages(bool dirtySynch = false); + plKey fOwner; + plKey fWorldKey; + float fHeight; float fRadius; - plKey fWorldKey; + plSimDefs::plLOSDB fLOSDB; - bool fSeeking; - bool fEnabled; - bool fEnableChanged; - bool fKinematic; - bool fKinematicEnableNextUpdate; - bool fKinematicChanged; - plMovementStrategySimulationInterface* fMovementInterface; - hsMatrix44 fLastGlobalLoc; - hsPoint3 fLocalPosition; + + plMovementStrategy* fMovementStrategy; + + float fSimLength; + hsQuat fLocalRotation; + hsPoint3 fLocalPosition; + hsPoint3 fLastLocalPosition; + + hsMatrix44 fLastGlobalLoc; hsMatrix44 fPrevSubworldW2L; - hsVector3 fDisplacementThisStep; - float fSimLength; - - //physical properties + hsVector3 fLinearVelocity; - float fAngularVelocity; hsVector3 fAchievedLinearVelocity; + plPhysical* fPushingPhysical; bool fFacingPushingPhysical; - bool fNeedsResize; + + bool fSeeking; + bool fEnabled; + bool fEnableChanged; }; -class plMovementStrategy: public plMovementStrategySimulationInterface +class plMovementStrategy { public: - virtual void SetControllerCore(plPhysicalControllerCore* core) - { - fCore=core; - fCore->SetMovementSimulationInterface(this); - } - virtual void RefreshConnectionToControllerCore() - { - fCore->SetMovementSimulationInterface(this); - //fCore->SetControllerDimensions(fPreferedControllerWidth,fPreferedControllerHeight); - fCore->BehaveLikeAnimatedPhysical(this->IRequireBehaviourLikeAnAnimatedPhysical()); - } - plMovementStrategy(plPhysicalControllerCore* core); - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void SetLinearAcceleration(const hsVector3& accel){fLinearAcceleration=accel;} - virtual const hsVector3& GetLinearAcceleration()const{return fLinearAcceleration;} - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void ResetAchievedLinearVelocity() - { - hsVector3 AchievedLinearVelocity(0.f,0.f,0.f); - if(fCore)fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - } -//proxy functions for Controller Core - virtual float GetAirTime() const { return fTimeInAir; } - virtual void ResetAirTime() { fTimeInAir = 0.f; } - + plMovementStrategy(plPhysicalControllerCore* controller); + virtual ~plMovementStrategy() { } + + virtual void Apply(float delSecs) = 0; + virtual void Update(float delSecs) { } + + virtual void AddContactNormals(hsVector3& vec) { } + virtual void Reset(bool newAge); + virtual bool IsKinematic() { return true; } + protected: - virtual bool IRequireBehaviourLikeAnAnimatedPhysical()=0; - virtual void IApplyKinematic(); - plPhysicalControllerCore* fCore; - hsVector3 fLinearAcceleration; - float fAngularAcceleration; - plKey fOwner; - static const float kAirTimeThreshold; - float fTimeInAir; - float fPreferedControllerWidth; - float fPreferedControllerHeight; - + plPhysicalControllerCore* fController; +}; + +class plAnimatedMovementStrategy : public plMovementStrategy +{ +public: + plAnimatedMovementStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plAnimatedMovementStrategy() { } + + void RecalcVelocity(double timeNow, float elapsed, bool useAnim = true); + void SetTurnStrength(float val) { fTurnStr = val; } + float GetTurnStrength() const { return fTurnStr; } + +private: + void IRecalcLinearVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); + void IRecalcAngularVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); + plAGApplicator* fRootApp; + hsVector3 fAnimLinearVel; + float fAnimAngularVel; + float fTurnStr; }; -class plWalkingStrategy: public plMovementStrategy +class plWalkingStrategy : public plAnimatedMovementStrategy { public: - plWalkingStrategy(plPhysicalControllerCore* core):plMovementStrategy(core) - { - fGroundHit=false; - fFalseGround=false; - fHitHead=false; - fCore->SetMovementSimulationInterface(this); - fPreferedControllerWidth=core->GetControllerWidth(); - fPreferedControllerHeight=core->GetControllerHeight(); - fOnTopOfAnimatedPhysLastFrame=false; - } - virtual ~plWalkingStrategy(){}; + plWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plWalkingStrategy() { } + virtual void Apply(float delSecs); - virtual void Update(float delSecs); + virtual void Update(float delSecs); + virtual void AddContactNormals(hsVector3& vec); + virtual void Reset(bool newAge); + + bool HitGroundInThisAge() const { return fHitGroundInThisAge; } bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } - bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } - void GroundHit() { fGroundHit = true; } - virtual void IAddContactNormals(hsVector3& vec); - virtual void StartJump(){}; - + + float GetAirTime() const { return fTimeInAir; } + void ResetAirTime() { fTimeInAir = 0.0f; } + + float GetImpactTime() const { return fImpactTime; } + const hsVector3& GetImpactVelocity() const { return fImpactVelocity; } + + bool EnableControlledFlight(bool status); + bool IsControlledFlight() const { return fControlledFlight != 0; } + + plPhysical* GetPushingPhysical() const; + bool GetFacingPushingPhysical() const; protected: - - void ICheckForFalseGround(); + static const float kAirTimeThreshold; + static const float kControlledFlightThreshold; + + hsTArray fSlidingNormals; + + hsVector3 fImpactVelocity; + float fImpactTime; + + float fTimeInAir; + + float fControlledFlightTime; + int fControlledFlight; + bool fGroundHit; bool fFalseGround; - bool fHitHead; - bool fOnTopOfAnimatedPhysLastFrame; - hsTArray fPrevSlidingNormals; - virtual bool IRequireBehaviourLikeAnAnimatedPhysical(){return true;} + bool fHeadHit; + bool fSliding; + bool fClearImpact; + bool fHitGroundInThisAge; }; -class plSwimStrategy: public plMovementStrategy + +class plSwimStrategy : public plAnimatedMovementStrategy { public: - plSwimStrategy(plPhysicalControllerCore *core); - virtual ~plSwimStrategy(){}; - void SetSurface(plSwimRegionInterface *region, float surfaceHeight); + plSwimStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plSwimStrategy() { } + virtual void Apply(float delSecs); - virtual void Update(float delSecs); - float GetBuoyancy() { return fBuoyancy; } - bool IsOnGround() { return fOnGround; } - bool HadContacts() { return fHadContacts; } - virtual void IAddContactNormals(hsVector3& vec); + + virtual void AddContactNormals(hsVector3& vec); + + void SetSurface(plSwimRegionInterface* region, float surfaceHeight); + + float GetBuoyancy() const { return fBuoyancy; } + bool IsOnGround() const { return fOnGround; } + bool HadContacts() const { return fHadContacts; } + protected: - virtual bool IRequireBehaviourLikeAnAnimatedPhysical(){return true;} -private: void IAdjustBuoyancy(); + float fBuoyancy; - bool fOnGround; - bool fHadContacts; float fSurfaceHeight; + plSwimRegionInterface *fCurrentRegion; + + bool fOnGround; + bool fHadContacts; }; -class plRidingAnimatedPhysicalStrategy : public plWalkingStrategy + +class plDynamicWalkingStrategy : public plWalkingStrategy { public: - plRidingAnimatedPhysicalStrategy(plPhysicalControllerCore *core ) : - fNeedVelocityOverride(false),fStartJump(false),plWalkingStrategy(core){}; - virtual ~plRidingAnimatedPhysicalStrategy(){}; + plDynamicWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plDynamicWalkingStrategy() { } + virtual void Apply(float delSecs); - virtual void Update(float delSecs); - virtual void PostStep(float delSecs); - bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } - bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } - void GroundHit() { fGroundHit = true; } - virtual void StartJump(){fStartJump = true;} + + virtual bool IsKinematic() { return false; } + protected: - virtual bool IRequireBehaviourLikeAnAnimatedPhysical(){return false;} - bool ICheckMove(const hsPoint3& startPos, const hsPoint3& desiredPos); - bool fNeedVelocityOverride; - hsVector3 fOverrideVelocity; - bool fStartJump; + bool ICheckForGround(float& zVelocity); }; + #endif// PLPHYSICALCONTROLLERCORE_H + diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp index 798e6c62..586c8301 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp @@ -70,7 +70,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plArmatureMod.h" #include "plAvatar/plAvBrain.h" #include "plAvatar/plAvatarMgr.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" #include "plModifier/plInterfaceInfoModifier.h" #include "pnModifier/plLogicModBase.h" #include "plVault/plVault.h" diff --git a/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp b/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp index 9de08fbc..85cb8b2b 100644 --- a/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp +++ b/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp @@ -54,7 +54,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plAvatarMsg.h" #include "plPhysical.h" #include "plPhysical/plSimDefs.h" -#include "plAvatar/plAvCallbackAction.h" #include "plAvatar/plAvBrainGeneric.h" diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp index 43cfef3f..eac1def9 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvatar/plAvCallbackAction.h" #include "HeadSpin.h" #include "plCollisionDetector.h" @@ -65,6 +64,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plAvatarMgr.h" #include "plAvatar/plAvBrainHuman.h" #include "plAvatar/plAvBrainDrive.h" +#include "plAvatar/plPhysicalControllerCore.h" #include "plModifier/plDetectorLog.h" @@ -475,7 +475,7 @@ void plObjectInVolumeAndFacingDetector::ICheckForTrigger() // And are we walking towards it? plArmatureBrain* abrain = armMod->FindBrainByClass(plAvBrainHuman::Index()); //armMod->GetCurrentBrain(); plAvBrainHuman* brain = plAvBrainHuman::ConvertNoRef(abrain); - if (brain && brain->IsMovingForward() && brain->fCallbackAction->IsOnGround()) + if (brain && brain->IsMovingForward() && brain->fWalkingStrategy->IsOnGround()) movingForward = true; } else @@ -645,11 +645,11 @@ bool plPanicLinkRegion::MsgReceive(plMessage* msg) { if (avMod->IsLinkedIn()) { - hsPoint3 kinPos; + hsPoint3 pos; if (avMod->GetController()) { - avMod->GetController()->GetKinematicPosition(kinPos); - DetectorLogSpecial("Avatar is panic linking. KinPos at %f,%f,%f and is %s",kinPos.fX,kinPos.fY,kinPos.fZ,avMod->GetController()->IsEnabled() ? "enabled" : "disabled"); + avMod->GetController()->GetPositionSim(pos); + DetectorLogSpecial("Avatar is panic linking. Position %f,%f,%f and is %s", pos.fX, pos.fY, pos.fZ, avMod->GetController()->IsEnabled() ? "enabled" : "disabled"); } avMod->PanicLink(fPlayLinkOutAnim); } else diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h b/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h index 4ece4ccc..81da4934 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h +++ b/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h @@ -72,25 +72,6 @@ namespace plSimDefs kGroupMax, }; - /** A taxonomy of action types. Crucial for doing things like making sure you don't - do things like attach duplicate actions. */ - enum ActionType - { - kUnknownAction = 0x01, // don't know the type (probably forgot to override GetType()) - kUnknownZAction = 0x02, // unknown type of z-order action - kAntiGravityAction = 0x03, // an action that counters gravity exactly - kUprightAction = 0x04, // an action that keeps an object upright by apply force - kPhysAnimAction = 0x05, // an action that parses keyframed animation into physical information - kConstraint = 0x06, // a general constraint. - kCallbackAction = 0x07, // an action that just hands us the physics "tick" - kPseudoPhysAction = 0x08, // replacement for the physAnim - kAntiGravAction = 0x09, // makes things float in the air - kBasicGroundAction = 0x0a, // for your basic walkAroundOnGround corrections - kHorizontalFreeze = 0x0b, // Let's you fall vertically, but otherwise keeps you in place (generic brains) - - kMaxAction = 0xffff // force 16-bit - }; - /** Different types of line-of-sight requests. */ enum LOSReqType { From 70e766ae5b346a8d1019ca5bfae0053206b1ecd5 Mon Sep 17 00:00:00 2001 From: Skoader Date: Sun, 24 Jun 2012 13:13:21 +1000 Subject: [PATCH 02/15] 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. */ From d4e9be28ec6e54d8f03649a37b7e68d3b076f0b2 Mon Sep 17 00:00:00 2001 From: Skoader Date: Wed, 11 Jul 2012 15:06:16 +1000 Subject: [PATCH 03/15] Remove some PhysX workarounds that are no longer required. --- .../PubUtilLib/plPhysX/plPXPhysical.cpp | 184 ------------------ .../Plasma/PubUtilLib/plPhysX/plPXPhysical.h | 33 ---- .../PubUtilLib/plPhysX/plSimulationMgr.cpp | 137 +------------ .../PubUtilLib/plPhysX/plSimulationMgr.h | 7 - 4 files changed, 8 insertions(+), 353 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp index 32d6ba18..55258d08 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp @@ -138,14 +138,10 @@ plPXPhysical::plPXPhysical() , fSceneNode(nil) , fWorldKey(nil) , fSndGroup(nil) - , fWorldHull(nil) - , fSaveTriangles(nil) - , fHullNumberPlanes(0) , fMass(0.f) , fWeWereHit(false) , fHitForce(0,0,0) , fHitPos(0,0,0) - , fInsideConvexHull(false) { } @@ -190,11 +186,6 @@ plPXPhysical::~plPXPhysical() plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); } - if (fWorldHull) - delete [] fWorldHull; - if (fSaveTriangles) - delete [] fSaveTriangles; - delete fProxyGen; // remove sdl modifier @@ -206,155 +197,6 @@ plPXPhysical::~plPXPhysical() delete fSDLMod; } -static void MakeBoxFromHull(NxConvexMesh* convexMesh, NxBoxShapeDesc& box) -{ - NxConvexMeshDesc desc; - convexMesh->saveToDesc(desc); - - float minX, minY, minZ, maxX, maxY, maxZ; - minX = minY = minZ = FLT_MAX; - maxX = maxY = maxZ = -FLT_MAX; - - for (int i = 0; i < desc.numVertices; i++) - { - float* point = (float*)(((char*)desc.points) + desc.pointStrideBytes*i); - float x = point[0]; - float y = point[1]; - float z = point[2]; - - minX = hsMinimum(minX, x); - minY = hsMinimum(minY, y); - minZ = hsMinimum(minZ, z); - maxX = hsMaximum(maxX, x); - maxY = hsMaximum(maxY, y); - maxZ = hsMaximum(maxZ, z); - } - - float xWidth = maxX - minX; - float yWidth = maxY - minY; - float zWidth = maxZ - minZ; - box.dimensions.x = xWidth / 2; - box.dimensions.y = yWidth / 2; - box.dimensions.z = zWidth / 2; - - //hsMatrix44 mat; - //box.localPose.getRowMajor44(&mat.fMap[0][0]); - hsPoint3 trans(minX + (xWidth / 2), minY + (yWidth / 2), minZ + (zWidth / 2)); - //mat.SetTranslate(&trans); - //box.localPose.setRowMajor44(&mat.fMap[0][0]); - - hsMatrix44 boxL2W; - boxL2W.Reset(); - boxL2W.SetTranslate(&trans); - plPXConvert::Matrix(boxL2W, box.localPose); - -} - -void plPXPhysical::IMakeHull(NxConvexMesh* convexMesh, hsMatrix44 l2w) -{ - NxConvexMeshDesc desc; - convexMesh->saveToDesc(desc); - - // make sure there are some triangles to work with - if (desc.numTriangles == 0) - return; - - // get rid of any we may have already had - if (fSaveTriangles) - delete [] fSaveTriangles; - - fHullNumberPlanes = desc.numTriangles; - fSaveTriangles = new hsPoint3[fHullNumberPlanes*3]; - - for (int i = 0; i < desc.numTriangles; i++) - { - uint32_t* triangle = (uint32_t*)(((char*)desc.triangles) + desc.triangleStrideBytes*i); - float* vertex1 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[0]); - float* vertex2 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[1]); - float* vertex3 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[2]); - hsPoint3 pt1(vertex1[0],vertex1[1],vertex1[2]); - hsPoint3 pt2(vertex2[0],vertex2[1],vertex2[2]); - hsPoint3 pt3(vertex3[0],vertex3[1],vertex3[2]); - - fSaveTriangles[(i*3)+0] = pt1; - fSaveTriangles[(i*3)+1] = pt2; - fSaveTriangles[(i*3)+2] = pt3; - } -} - -void plPXPhysical::ISetHullToWorldWTriangles() -{ - // if we have a detector hull and the world hasn't been updated - if (fWorldHull == nil) - { - fWorldHull = new hsPlane3[fHullNumberPlanes]; - // use the local2world from the physics engine so that it matches the transform of the positions from the triggerees - hsMatrix44 l2w; - plPXConvert::Matrix(fActor->getGlobalPose(), l2w); - int i; - for( i = 0; i < fHullNumberPlanes; i++ ) - { - hsPoint3 pt1 = fSaveTriangles[i*3]; - hsPoint3 pt2 = fSaveTriangles[(i*3)+1]; - hsPoint3 pt3 = fSaveTriangles[(i*3)+2]; - - // local to world translation - pt1 = l2w * pt1; - pt2 = l2w * pt2; - pt3 = l2w * pt3; - - hsPlane3 plane(&pt1, &pt2, &pt3); - fWorldHull[i] = plane; - } - } -} - - -bool plPXPhysical::IsObjectInsideHull(const hsPoint3& pos) -{ - if (fSaveTriangles) - { - ISetHullToWorldWTriangles(); - int i; - for( i = 0; i < fHullNumberPlanes; i++ ) - { - if (!ITestPlane(pos, fWorldHull[i])) - return false; - } - return true; - } - return false; -} - -bool plPXPhysical::Should_I_Trigger(bool enter, hsPoint3& pos) -{ - // see if we are inside the detector hull, if so, then don't trigger - bool trigger = false; - bool inside = IsObjectInsideHull(pos); - if ( !inside) - { - trigger = true; - fInsideConvexHull = enter; - } - else - { - // catch those rare cases on slow machines that miss the collision before avatar penetrated the face - if (enter && !fInsideConvexHull) - { -#ifdef PHYSX_SAVE_TRIGGERS_WORKAROUND - trigger = true; - fInsideConvexHull = enter; - DetectorLogSpecial("**>Saved a missing enter collision: %s",GetObjectKey()->GetName().c_str()); -#else - DetectorLogSpecial("**>Could have saved a missing enter collision: %s",GetObjectKey()->GetName().c_str()); -#endif PHYSX_SAVE_TRIGGERS_WORKAROUND - } - } - - return trigger; -} - - bool plPXPhysical::Init(PhysRecipe& recipe) { bool startAsleep = false; @@ -388,29 +230,6 @@ bool plPXPhysical::Init(PhysRecipe& recipe) } break; case plSimDefs::kHullBounds: - // FIXME PHYSX - Remove when hull detection is fixed - // If this is read time (ie, meshStream is nil), turn the convex hull - // into a box. That way the data won't have to change when convex hulls - // actually work right. - if (fGroup == plSimDefs::kGroupDetector && recipe.meshStream == nil) - { -#ifdef USE_BOXES_FOR_DETECTOR_HULLS - MakeBoxFromHull(recipe.convexMesh, boxDesc); - plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*recipe.convexMesh); - boxDesc.group = fGroup; - actorDesc.shapes.push_back(&boxDesc); -#else -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - // make a hull of planes for testing IsInside - IMakeHull(recipe.convexMesh,recipe.l2s); -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - convexShapeDesc.meshData = recipe.convexMesh; - convexShapeDesc.userData = recipe.meshStream; - convexShapeDesc.group = fGroup; - actorDesc.shapes.pushBack(&convexShapeDesc); -#endif // USE_BOXES_FOR_DETECTOR_HULLS - } - else { convexShapeDesc.meshData = recipe.convexMesh; convexShapeDesc.userData = recipe.meshStream; @@ -669,9 +488,6 @@ void plPXPhysical::IEnable(bool enable) { fActor->clearActorFlag(NX_AF_DISABLE_COLLISION); - // PHYSX FIXME - after re-enabling a possible detector, we need to check to see if any avatar is already in the PhysX turdy hull detector region - plSimulationMgr::GetInstance()->UpdateAvatarInDetector(fWorldKey, this); - if (fActor->isDynamic()) fActor->clearBodyFlag(NX_BF_FROZEN); else diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h index 693fbc97..07f407e4 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h @@ -156,11 +156,6 @@ public: virtual uint16_t GetAllLOSDBs() { return fLOSDBs; } virtual bool IsInLOSDB(uint16_t flag) { return hsCheckBits(fLOSDBs, flag); } - virtual bool DoDetectorHullWorkaround() { return fSaveTriangles ? true : false; } - virtual bool Should_I_Trigger(bool enter, hsPoint3& pos); - virtual bool IsObjectInsideHull(const hsPoint3& pos); - virtual void SetInsideConvexHull(bool inside) { fInsideConvexHull = inside; } - virtual plKey GetWorldKey() const { return fWorldKey; } virtual plPhysicalSndGroup* GetSoundGroup() const { return fSndGroup; } @@ -201,14 +196,6 @@ protected: /** Handle messages about our references. */ bool HandleRefMsg(plGenRefMsg * refM); - ///////////////////////////////////////////////////////////// - // - // WORLDS, SUBWORLDS && CONTEXTS - // - ///////////////////////////////////////////////////////////// - - void IConvertGroups(uint32_t memberOf, uint32_t reportsOn, uint32_t collideWith); - /** See if the object is in a valid, non-overlapping position. A valid overlap is one which is approved by the collision masking code, i.e. my memberOf has no intersection with your @@ -236,8 +223,6 @@ protected: // Enable/disable collisions and dynamic movement void IEnable(bool enable); - void IMakeHull(NxConvexMesh* convexMesh, hsMatrix44 l2w); - NxActor* fActor; plKey fWorldKey; // either a subworld or nil @@ -251,24 +236,6 @@ protected: plKey fObjectKey; // the key to our scene object plKey fSceneNode; // the room we're in - // PHYSX FIXME - need to create a plasma hull so that we can determine if inside - hsPlane3* fWorldHull; - uint32_t fHullNumberPlanes; - hsPoint3* fSaveTriangles; - bool fInsideConvexHull; - void ISetHullToWorldWTriangles(); - inline bool ITestPlane(const hsPoint3 &pos, const hsPlane3 &plane) - { - float dis = plane.fN.InnerProduct(pos); - dis += plane.fD; - if (dis == 0.f) - return false; - if( dis >= 0.f ) - return false; - - return true; - } - // we need to remember the last matrices we sent to the coordinate interface // so that we can recognize them when we send them back and not reapply them, // which would reactivate our body. inelegant but effective diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index c7156c4f..d7d1d5d4 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -95,60 +95,18 @@ class SensorReport : public NxUserTriggerReport if (doReport) { -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - if ( triggerPhys->DoDetectorHullWorkaround() ) + if (status & NX_TRIGGER_ON_ENTER) { - if (status & NX_TRIGGER_ON_ENTER && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); - } - else if (status & NX_TRIGGER_ON_ENTER) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName().c_str()); - } - if (status & NX_TRIGGER_ON_LEAVE && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); - } - else if (status & NX_TRIGGER_ON_LEAVE) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName().c_str()); - } - if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed event(CH)",triggerPhys->GetObjectKey()->GetName().c_str()); - } + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision %s enter",triggerPhys->GetObjectKey()->GetName().c_str()); + plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); } - else + else if (status & NX_TRIGGER_ON_LEAVE) { -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - if (status & NX_TRIGGER_ON_ENTER) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); - } - if (status & NX_TRIGGER_ON_LEAVE) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); - } - if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed event",triggerPhys->GetObjectKey()->GetName().c_str()); - } -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision %s exit",triggerPhys->GetObjectKey()->GetName().c_str()); + plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); } -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND } } } gSensorReport; @@ -459,85 +417,6 @@ void plSimulationMgr::ReleaseScene(plKey world) } } -void plSimulationMgr::ISendCollisionMsg(plKey receiver, plKey hitter, bool entering) -{ - DetectorLogYellow("Collision: %s is inside %s. Sending an %s msg", hitter ? hitter->GetName().c_str() : "(nil)", - receiver->GetName().c_str(), entering ? "'enter'" : "'exit'"); - plCollideMsg* msg = new plCollideMsg; - msg->fOtherKey = hitter; - msg->fEntering = entering; - msg->AddReceiver(receiver); - msg->Send(); -} - -void plSimulationMgr::UpdateDetectorsInScene(plKey world, plKey avatar, hsPoint3& pos, bool entering) -{ - // search thru the actors in a scene looking for convex hull detectors and see if the avatar is inside it - // ... and then send appropiate collision message if needed - NxScene* scene = GetScene(world); - plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded()); - const plCoordinateInterface* ci = avObj->GetCoordinateInterface(); - hsPoint3 soPos = ci->GetWorldPos(); - if (scene) - { - uint32_t numActors = scene->getNbActors(); - NxActor** actors = scene->getActors(); - - for (int i = 0; i < numActors; i++) - { - plPXPhysical* physical = (plPXPhysical*)actors[i]->userData; - if (physical && physical->DoDetectorHullWorkaround()) - { - if ( physical->IsObjectInsideHull(pos) ) - { - physical->SetInsideConvexHull(entering); - // we are entering this world... say we entered this detector - ISendCollisionMsg(physical->GetObjectKey(), avatar, entering); - } - } - } - } -} - -void plSimulationMgr::UpdateAvatarInDetector(plKey world, plPXPhysical* detector) -{ - // search thru the actors in a scene looking for avatars that might be in the newly enabled detector region - // ... and then send appropiate collision message if needed - if ( detector->DoDetectorHullWorkaround() ) - { - NxScene* scene = GetScene(world); - if (scene) - { - uint32_t numActors = scene->getNbActors(); - NxActor** actors = scene->getActors(); - - for (int i = 0; i < numActors; i++) - { - if ( actors[i]->userData == nil ) - { - // we go a controller - plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(*actors[i]); - if (controller && controller->IsEnabled()) - { - plKey avatar = controller->GetOwner(); - plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded()); - const plCoordinateInterface* ci; - if ( avObj && ( ci = avObj->GetCoordinateInterface() ) ) - { - if ( detector->IsObjectInsideHull(ci->GetWorldPos()) ) - { - detector->SetInsideConvexHull(true); - // we are entering this world... say we entered this detector - ISendCollisionMsg(detector->GetObjectKey(), avatar, true); - } - } - } - } - } - } - } -} - void plSimulationMgr::AddCollisionMsg(plKey hitee, plKey hitter, bool enter) { // First, make sure we have no dupes diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h index 552ccbff..230429c7 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h @@ -99,10 +99,6 @@ public: int GetMaterialIdx(NxScene* scene, float friction, float restitution); - // PHYSX FIXME - walk thru all the convex hull detector regions to see if we are in any... we're either coming or going - void UpdateDetectorsInScene(plKey world, plKey avatar, hsPoint3& pos, bool entering); - void UpdateAvatarInDetector(plKey world, plPXPhysical* detector); - //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); @@ -122,9 +118,6 @@ protected: // Walk through the synchronization requests and send them as appropriate. void IProcessSynchs(); - // PHYSX FIXME send a collision message - should only be used with UpdateDetectorsInScene - void ISendCollisionMsg(plKey receiver, plKey hitter, bool entering); - NxPhysicsSDK* fSDK; plPhysicsSoundMgr* fSoundMgr; From 5522a10a0c11f7d4739d27b88c9db5f6ca9329b1 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sat, 1 Dec 2012 21:51:05 -0500 Subject: [PATCH 04/15] Fix horizontal mouse-look while standing still. --- .../PubUtilLib/plAvatar/plPhysicalControllerCore.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index ed3b13d4..af69775b 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -253,11 +253,6 @@ void plAnimatedMovementStrategy::RecalcVelocity(double timeNow, float elapsed, b IRecalcLinearVelocity(elapsed, prevMat, curMat); IRecalcAngularVelocity(elapsed, prevMat, curMat); - - // Update controller rotation - float zRot = fAnimAngularVel + fTurnStr; - if (hsABS(zRot) > 0.0001f) - fController->IncrementAngle(zRot * elapsed); } else { @@ -265,6 +260,11 @@ void plAnimatedMovementStrategy::RecalcVelocity(double timeNow, float elapsed, b fAnimAngularVel = 0.0f; } + // Update controller rotation + float zRot = fAnimAngularVel + fTurnStr; + if (hsABS(zRot) > 0.0001f) + fController->IncrementAngle(zRot * elapsed); + // Update controller velocity fController->SetLinearVelocity(fAnimLinearVel); } From 462d4d690660184de50d8885b2c119e026217cab Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 21:55:47 -0500 Subject: [PATCH 05/15] Improve simulation timing at low frame rates. Increased kDefaultMaxDelta to 0.15. Added a small bias to combat a truncation issue. --- Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index d7d1d5d4..19a78afc 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -193,7 +193,7 @@ class ErrorStream : public NxUserOutputStream // ///////////////////////////////////////////////////////////////// -#define kDefaultMaxDelta (0.1) // if the step is greater than .1 seconds, clamp to that +#define kDefaultMaxDelta (0.15) // if the step is greater than .15 seconds, clamp to that #define kDefaultStepSize (1.f / 60.f) // default simulation freqency is 60hz ///////////////////////////////////////////////////////////////// @@ -469,7 +469,7 @@ void plSimulationMgr::Advance(float delSecs) } // Perform as many whole substeps as possible saving the remainder in our accumulator. - int numSubSteps = (int)(fAccumulator / kDefaultStepSize); + int numSubSteps = (int)(fAccumulator / kDefaultStepSize + 0.000001f); float delta = numSubSteps * kDefaultStepSize; fAccumulator -= delta; From b5aaecc48de40f4d6e4cfd1e7d13dac9078c3a17 Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 22:03:16 -0500 Subject: [PATCH 06/15] Fix subworld transition bug Use the actual global location, not the interpolated location when moving between subworlds. Rebuild the controller cache. --- .../plPhysX/plPXPhysicalControllerCore.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp index edbb6808..80866eed 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp @@ -236,6 +236,25 @@ void plPXPhysicalControllerCore::SetSubworld(plKey world) IInformDetectors(false); IDeleteController(); + // We need our real global location here, not the interpolated location + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&fLocalPosition); + if (fWorldKey) + { + hsMatrix44 prevSubL2W; + fPrevSubworldW2L.GetInverse(&prevSubL2W); + fLastGlobalLoc = prevSubL2W * fLastGlobalLoc; + } + // Update our scene object so the change isn't wiped out + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + hsMatrix44 globalLocInv; + fLastGlobalLoc.GetInverse(&globalLocInv); + so->SetTransform(fLastGlobalLoc, globalLocInv); + so->FlushTransform(); + } + // Update Local Position and rotation fWorldKey = world; const plCoordinateInterface* subworldCI = GetSubworldCI(); @@ -257,6 +276,7 @@ void plPXPhysicalControllerCore::SetSubworld(plKey world) // Create new controller ICreateController(fLocalPosition); + RebuildCache(); } } From 47efb94aa88c859736e432938e452d8e427121cd Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 22:09:53 -0500 Subject: [PATCH 07/15] Fix warping an avatar triggers all detectors along the path When explicitly moving an avatar over the given threshold, teleport the underlying actor most of the way before moving the controller. --- .../plPhysX/plPXPhysicalControllerCore.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp index 80866eed..127df59c 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp @@ -334,6 +334,7 @@ void plPXPhysicalControllerCore::SetGlobalLoc(const hsMatrix44& l2w) fLastGlobalLoc = l2w; // Update our local position and rotation + hsPoint3 prevPosition = fLocalPosition; const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { @@ -360,8 +361,20 @@ void plPXPhysicalControllerCore::SetGlobalLoc(const hsMatrix44& l2w) // Update the physical position if (fKinematicCCT) { - NxExtendedVec3 pos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kCCTZOffset); - fController->setPosition(pos); + hsVector3 disp(&fLocalPosition, &prevPosition); + if (disp.Magnitude() > 2.f) + { + // Teleport the underlying actor most of the way + disp.Normalize(); + disp *= 0.001f; + + hsPoint3 teleportPos = fLocalPosition - disp; + NxVec3 pos(teleportPos.fX, teleportPos.fY, teleportPos.fZ + kPhysZOffset); + fActor->setGlobalPosition(pos); + } + + NxExtendedVec3 extPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kCCTZOffset); + fController->setPosition(extPos); } else { From 2f25e0490918bda5f918388bfd2ab23a5021c7ee Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 22:40:26 -0500 Subject: [PATCH 08/15] Fix controlled flight bug fControlledFlight state should be handled outside of the physics step. --- .../plAvatar/plPhysicalControllerCore.cpp | 23 +++++++++++-------- .../plAvatar/plPhysicalControllerCore.h | 4 +++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index af69775b..bb15544e 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -503,15 +503,6 @@ void plWalkingStrategy::Update(float delSecs) fImpactVelocity = (hsVector3)fController->GetLocalRotation().Rotate(&fImpactVelocity); fClearImpact = false; } - - if (fControlledFlight != 0) - { - if (IsOnGround()) - fControlledFlightTime = fTimeInAir; - - if (fControlledFlightTime > kControlledFlightThreshold) - EnableControlledFlight(false); - } } void plWalkingStrategy::AddContactNormals(hsVector3& vec) @@ -535,6 +526,20 @@ void plWalkingStrategy::Reset(bool newAge) } } +void plWalkingStrategy::RecalcVelocity(double timeNow, float elapsed, bool useAnim) +{ + if (fControlledFlight != 0) + { + if (IsOnGround()) + fControlledFlightTime = fTimeInAir; + + if (fControlledFlightTime > kControlledFlightThreshold) + EnableControlledFlight(false); + } + + plAnimatedMovementStrategy::RecalcVelocity(timeNow, elapsed, useAnim); +} + bool plWalkingStrategy::EnableControlledFlight(bool status) { if (status) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h index 808a6702..97af7f48 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h @@ -214,7 +214,7 @@ public: plAnimatedMovementStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); virtual ~plAnimatedMovementStrategy() { } - void RecalcVelocity(double timeNow, float elapsed, bool useAnim = true); + virtual void RecalcVelocity(double timeNow, float elapsed, bool useAnim = true); void SetTurnStrength(float val) { fTurnStr = val; } float GetTurnStrength() const { return fTurnStr; } @@ -240,6 +240,8 @@ public: virtual void AddContactNormals(hsVector3& vec); virtual void Reset(bool newAge); + virtual void RecalcVelocity(double timeNow, float elapsed, bool useAnim = true); + bool HitGroundInThisAge() const { return fHitGroundInThisAge; } bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } From 01d47450e6c492e5ccaf667e7ff86c17e77ffcef Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 22:59:24 -0500 Subject: [PATCH 09/15] Disabled avatar updates Only update disabled avatars between steps if in a subworld and then don't interpolate their position. --- .../plAvatar/plPhysicalControllerCore.cpp | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index bb15544e..75d1511e 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -195,20 +195,38 @@ void plPhysicalControllerCore::IUpdateNonPhysical(float alpha) const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (CompareMatrices(fLastGlobalLoc, l2w, 0.0001f)) { - hsVector3 displacement = (hsVector3)(fLocalPosition - fLastLocalPosition); - hsPoint3 interpLocalPos = fLastLocalPosition + (displacement * alpha); - - fLocalRotation.MakeMatrix(&fLastGlobalLoc); - fLastGlobalLoc.SetTranslate(&interpLocalPos); - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) + if (fEnabled) { - const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); - fLastGlobalLoc = subL2W * fLastGlobalLoc; - fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + hsVector3 displacement = (hsVector3)(fLocalPosition - fLastLocalPosition); + hsPoint3 interpLocalPos = fLastLocalPosition + (displacement * alpha); + + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&interpLocalPos); + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); + fLastGlobalLoc = subL2W * fLastGlobalLoc; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + } + + ISendCorrectionMessages(); } + else + { + // Update global location if in a subworld + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + hsMatrix44 l2s = fPrevSubworldW2L * fLastGlobalLoc; + const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); + fLastGlobalLoc = subL2W * l2s; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); - ISendCorrectionMessages(); + + ISendCorrectionMessages(); + } + } } } From ae594dcc8146d447771a52bf2deb9b0154941e54 Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 23:05:19 -0500 Subject: [PATCH 10/15] Clear linear velocity after each update. --- .../Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index 75d1511e..0b9060f3 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -502,6 +502,9 @@ void plWalkingStrategy::Update(float delSecs) } } + hsVector3 zeroVelocity(0.f, 0.f, 0.f); + fController->SetLinearVelocity(zeroVelocity); + if (!fHitGroundInThisAge && IsOnGround()) fHitGroundInThisAge = true; From e29e90833394c68f0c9a91d476524aeccf9441e4 Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 23:09:46 -0500 Subject: [PATCH 11/15] Clear impact velocity on WalkingStrategy Reset. Fixes landing behaviours incorrectly firing after a brain transition. --- Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index 0b9060f3..ad83a249 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -538,6 +538,8 @@ void plWalkingStrategy::AddContactNormals(hsVector3& vec) void plWalkingStrategy::Reset(bool newAge) { plMovementStrategy::Reset(newAge); + fImpactVelocity.Set(0.0f, 0.0f, 0.0f); + fImpactTime = 0.0f; if (newAge) { fTimeInAir = 0.0f; From a1814c9dca09356d73ce2c69e823dd3eb84d26b3 Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 23:11:03 -0500 Subject: [PATCH 12/15] Clear achieved linear velocity on disabled avatars. --- Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index ad83a249..6fa701fc 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -172,6 +172,8 @@ void plPhysicalControllerCore::IUpdate(int numSubSteps, float alpha) } else { + fAchievedLinearVelocity.Set(0.0f, 0.0f, 0.0f); + // Update global location if in a subworld const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) From d06ba6362ccfe981a28de3b11270d84e2140e8d7 Mon Sep 17 00:00:00 2001 From: Skoader Date: Sat, 1 Dec 2012 23:16:48 -0500 Subject: [PATCH 13/15] Only apply height correction to human avatars. --- .../PubUtilLib/plAvatar/plArmatureMod.cpp | 2 +- .../plAvatar/plPhysicalControllerCore.h | 2 +- .../plPhysX/plPXPhysicalControllerCore.cpp | 17 +++++++++++------ .../plPhysX/plPXPhysicalControllerCore.h | 3 ++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index f155d6ef..cf0d2cdf 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -1993,7 +1993,7 @@ bool plArmatureMod::ValidatePhysics() return false; if (!fController) - fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, fPhysWidth); + fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, fPhysWidth, (fBodyType == kBoneBaseMale || fBodyType == kBoneBaseFemale)); if (fController) { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h index 97af7f48..b0c3f9a7 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h @@ -150,7 +150,7 @@ public: float GetHeight() const { return fHeight; } // Create a new controller instance - Implemented in the physics system - static plPhysicalControllerCore* Create(plKey ownerSO, float height, float radius); + static plPhysicalControllerCore* Create(plKey ownerSO, float height, float radius, bool human); protected: virtual void IHandleEnableChanged() = 0; diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp index 127df59c..ef595a72 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp @@ -155,23 +155,24 @@ public: } } gControllerHitReport; -plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, float height, float width) +plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, float height, float width, bool human) { if (!plPXPhysicalControllerCore::fPXControllersMax || gControllers.size() < plPXPhysicalControllerCore::fPXControllersMax) { float radius = width / 2.0f; float realHeight = height - width; - return new plPXPhysicalControllerCore(ownerSO, realHeight, radius); + return new plPXPhysicalControllerCore(ownerSO, realHeight, radius, human); } return nil; } -plPXPhysicalControllerCore::plPXPhysicalControllerCore(plKey ownerSO, float height, float radius) +plPXPhysicalControllerCore::plPXPhysicalControllerCore(plKey ownerSO, float height, float radius, bool human) : plPhysicalControllerCore(ownerSO, height, radius), fController(nil), fActor(nil), fProxyGen(nil), - fKinematicCCT(true) + fKinematicCCT(true), + fHuman(human) { ICreateController(fLocalPosition); fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); @@ -724,10 +725,14 @@ void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos) // 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; + float kineHeight = fHeight; NxCapsuleShape* capShape = shape->isCapsule(); + if (fHuman) + { + kineHeight += kPhysHeightCorrection; + capShape->setLocalPosition(NxVec3(0.0f, (kPhysHeightCorrection / 2.0f), 0.0f)); + } capShape->setDimensions(kineRadius, kineHeight); - capShape->setLocalPosition(NxVec3(0.0f, (kPhysHeightCorrection / 2.0f), 0.0f)); } else { diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h index 4e80b6ff..70ff44d1 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h @@ -65,7 +65,7 @@ public: class plPXPhysicalControllerCore: public plPhysicalControllerCore { public: - plPXPhysicalControllerCore(plKey ownerSO, float height, float radius); + plPXPhysicalControllerCore(plKey ownerSO, float height, float radius, bool human); ~plPXPhysicalControllerCore(); // An ArmatureMod has its own idea about when physics should be enabled/disabled. @@ -169,4 +169,5 @@ protected: plPhysicalProxy* fProxyGen; bool fKinematicCCT; + bool fHuman; }; From d3ea8fe33370fa685ffa11dfdfcea342921d93ae Mon Sep 17 00:00:00 2001 From: Skoader Date: Sun, 2 Dec 2012 01:02:49 -0500 Subject: [PATCH 14/15] Rework PhysX collision flutter bug workaround To better handle erroneous trigger events under the new timing method, use the number of times the simulation has actually advanced instead of the number of evals received. Cleaned up a bit. --- .../PubUtilLib/plPhysX/plSimulationMgr.cpp | 3 + .../PubUtilLib/plPhysX/plSimulationMgr.h | 3 + .../plPhysical/plCollisionDetector.cpp | 221 ++++++++++-------- .../plPhysical/plCollisionDetector.h | 55 +++-- 4 files changed, 167 insertions(+), 115 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index 19a78afc..629163d6 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -283,6 +283,7 @@ plSimulationMgr* plSimulationMgr::GetInstance() plSimulationMgr::plSimulationMgr() : fSuspended(true) , fAccumulator(0.0f) + , fStepCount(0) , fLOSDispatch(new plLOSDispatch()) , fSoundMgr(new plPhysicsSoundMgr) , fLog(nil) @@ -468,6 +469,8 @@ void plSimulationMgr::Advance(float delSecs) fAccumulator = kDefaultMaxDelta; } + ++fStepCount; + // Perform as many whole substeps as possible saving the remainder in our accumulator. int numSubSteps = (int)(fAccumulator / kDefaultStepSize + 0.000001f); float delta = numSubSteps * kDefaultStepSize; diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h index 230429c7..d8a38b50 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h @@ -99,6 +99,8 @@ public: int GetMaterialIdx(NxScene* scene, float friction, float restitution); + uint32_t GetStepCount() const { return fStepCount; } + //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); @@ -139,6 +141,7 @@ protected: bool fSuspended; float fAccumulator; + uint32_t fStepCount; // 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) diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp index eac1def9..ca14aa0b 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp @@ -68,8 +68,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plModifier/plDetectorLog.h" -#define USE_PHYSX_MULTIPLE_CAMREGION_ENTER 1 -#define USE_PHYSX_COLLISION_FLUTTER_WORKAROUND 1 +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND +#include "plPhysX/plSimulationMgr.h" +#endif + + plArmatureMod* plCollisionDetector::IGetAvatarModifier(plKey key) { @@ -240,58 +243,19 @@ plCameraRegionDetector::~plCameraRegionDetector() hsRefCnt_SafeUnRef(*it); } -void plCameraRegionDetector::ITrigger(plKey hitter, bool entering, bool immediate) +void plCameraRegionDetector::ISendTriggerMsg() { - if (fSavingSendMsg) - DetectorLogRed("%s: Stale messages on ITrigger. This should never happen!", GetKeyName().c_str()); - if (fIsInside && entering) - DetectorLogRed("%s: Duplicate enter! Did we miss an exit?", GetKeyName().c_str()); - else if (!fIsInside && !entering) - DetectorLogRed("%s: Duplicate exit! Did we miss an enter?", GetKeyName().c_str()); - - fSavingSendMsg = true; - fSavedMsgEnterFlag = entering; - if (entering) - { - DetectorLog("%s: Saving camera Entering volume - Evals=%d", GetKeyName().c_str(),fNumEvals); - fLastEnterEval = fNumEvals; - } - else - { - DetectorLog("%s: Saving camera Exiting volume - Evals=%d", GetKeyName().c_str(),fNumEvals); - fLastExitEval = fNumEvals; - } - - if (immediate) - ISendSavedTriggerMsgs(); -} - -void plCameraRegionDetector::ISendSavedTriggerMsgs() -{ - if (fSavingSendMsg) + for (plCameraMsgVec::iterator it = fMessages.begin(); it != fMessages.end(); ++it) { - for (size_t i = 0; i < fMessages.size(); ++i) - { - hsRefCnt_SafeRef(fMessages[i]); - if (fSavedMsgEnterFlag) - { - fMessages[i]->SetCmd(plCameraMsg::kEntering); - DetectorLog("Entering cameraRegion: %s - Evals=%d -msg %d of %d\n", GetKeyName().c_str(),fNumEvals,i+1,fMessages.size()); - fIsInside = true; - } - else - { - fMessages[i]->ClearCmd(plCameraMsg::kEntering); - DetectorLog("Exiting cameraRegion: %s - Evals=%d -msg %d of %d\n", GetKeyName().c_str(),fNumEvals,i+1,fMessages.size()); - fIsInside = false; - } - plgDispatch::MsgSend(fMessages[i]); - } + plCameraMsg* msg = *it; + if (fIsInside) + msg->SetCmd(plCameraMsg::kEntering); + else + msg->ClearCmd(plCameraMsg::kEntering); + msg->SendAndKeep(); } - fSavingSendMsg = false; } - bool plCameraRegionDetector::MsgReceive(plMessage* msg) { plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); @@ -300,8 +264,16 @@ bool plCameraRegionDetector::MsgReceive(plMessage* msg) // camera collisions are only for the local player if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey) return true; - // Fall through to plObjectInVolumeDetector, which will register us for plEvalMsg - // and handle it for us. (Hint: See ISendSavedTriggerMsgs) + + if (!fWaitingForEval) + IRegisterForEval(); + + fEntering = (pCollMsg->fEntering != 0); + + #ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + fLastStep = plSimulationMgr::GetInstance()->GetStepCount(); +#endif + return true; } return plObjectInVolumeDetector::MsgReceive(msg); @@ -327,56 +299,72 @@ void plCameraRegionDetector::Write(hsStream* stream, hsResMgr* mgr) } +void plCameraRegionDetector::IHandleEval(plEvalMsg*) +{ +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + if (plSimulationMgr::GetInstance()->GetStepCount() - fLastStep > 1) + { +#endif + if (fIsInside != fEntering) + { + fIsInside = fEntering; + DetectorLog("%s CameraRegion: %s", fIsInside ? "Entering" : "Exiting", GetKeyName().c_str()); + ISendTriggerMsg(); + } + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + fWaitingForEval = false; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + } +#endif +} + ///////////////////////////////// ///////////////////////////////// ///////////////////////////////// ///////////////////////////////// // object-in-volume detector -void plObjectInVolumeDetector::ITrigger(plKey hitter, bool entering, bool immediate) +void plObjectInVolumeDetector::ITrigger(plKey hitter, bool entering) { - hsRefCnt_SafeUnRef(fSavedActivatorMsg); - fSavedActivatorMsg = new plActivatorMsg; - fSavedActivatorMsg->AddReceivers(fReceivers); - - if (fProxyKey) - fSavedActivatorMsg->fHiteeObj = fProxyKey; - else - fSavedActivatorMsg->fHiteeObj = GetTarget()->GetKey(); - - fSavedActivatorMsg->fHitterObj = hitter; - fSavedActivatorMsg->SetSender(GetKey()); - - if (entering) +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + for (bookKeepingList::iterator it = fCollisionList.begin(); it != fCollisionList.end(); ++it) { - DetectorLog("%s: Saving Entering volume - Evals=%d", GetKeyName().c_str(), fNumEvals); - fSavedActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeEnter); - fLastEnterEval = fNumEvals; - } - else - { - DetectorLog("%s: Saving Exiting volume - Evals=%d", GetKeyName().c_str(), fNumEvals); - fSavedActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeExit); - fLastExitEval = fNumEvals; + plCollisionBookKeepingInfo* collisionInfo = *it; + if (collisionInfo->fHitter == hitter) + { + collisionInfo->fEntering = entering; + collisionInfo->fLastStep = plSimulationMgr::GetInstance()->GetStepCount(); + return; + } } +#endif - if (immediate) - ISendSavedTriggerMsgs(); + plCollisionBookKeepingInfo* collisionInfo = new plCollisionBookKeepingInfo(hitter, entering); + fCollisionList.push_back(collisionInfo); +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + collisionInfo->fLastStep = plSimulationMgr::GetInstance()->GetStepCount(); +#endif } -void plObjectInVolumeDetector::ISendSavedTriggerMsgs() +void plObjectInVolumeDetector::IRegisterForEval() { - if (fSavedActivatorMsg) - { - if (fSavedActivatorMsg->fTriggerType == plActivatorMsg::kVolumeEnter) - DetectorLog("%s: Sending Entering volume - Evals=%d", GetKeyName().c_str(), fNumEvals); - else - DetectorLog("%s: Sending Exiting volume - Evals=%d", GetKeyName().c_str(), fNumEvals); + fWaitingForEval = true; + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} - // we're saving the message to be dispatched later... - plgDispatch::MsgSend(fSavedActivatorMsg); - } - fSavedActivatorMsg = nil; +void plObjectInVolumeDetector::ISendTriggerMsg(plKey hitter, bool entering) +{ + plActivatorMsg* activatorMsg = new plActivatorMsg(); + activatorMsg->SetSender(GetKey()); + activatorMsg->AddReceivers(fReceivers); + activatorMsg->fHiteeObj = fProxyKey ? fProxyKey : GetTarget()->GetKey(); + activatorMsg->fHitterObj = hitter; + if (entering) + activatorMsg->SetTriggerType(plActivatorMsg::kVolumeEnter); + else + activatorMsg->SetTriggerType(plActivatorMsg::kVolumeExit); + + plgDispatch::MsgSend(activatorMsg); } bool plObjectInVolumeDetector::MsgReceive(plMessage* msg) @@ -387,18 +375,17 @@ bool plObjectInVolumeDetector::MsgReceive(plMessage* msg) // If the avatar is disabled (flying around), don't trigger if (IIsDisabledAvatar(pCollMsg->fOtherKey)) return false; + + if (!fWaitingForEval) + IRegisterForEval(); + ITrigger(pCollMsg->fOtherKey, (pCollMsg->fEntering != 0)); - plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); return true; } plEvalMsg* pEvalMsg = plEvalMsg::ConvertNoRef(msg); if (pEvalMsg) - { - fNumEvals++; - ISendSavedTriggerMsgs(); - plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); - } + IHandleEval(pEvalMsg); plPlayerPageMsg* pageMsg = plPlayerPageMsg::ConvertNoRef(msg); if (pageMsg && pageMsg->fUnload) @@ -409,6 +396,48 @@ bool plObjectInVolumeDetector::MsgReceive(plMessage* msg) return plCollisionDetector::MsgReceive(msg); } +void plObjectInVolumeDetector::IHandleEval(plEvalMsg*) +{ + bookKeepingList::iterator it = fCollisionList.begin(); + while (it != fCollisionList.end()) + { + plCollisionBookKeepingInfo* collisionInfo = *it; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + if (plSimulationMgr::GetInstance()->GetStepCount() - collisionInfo->fLastStep > 1) + { +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + ResidentSet::iterator j = fCurrentResidents.find(collisionInfo->fHitter); + bool wasInside = j != fCurrentResidents.end(); + if (collisionInfo->fEntering != wasInside) + { + if (collisionInfo->fEntering) + { + fCurrentResidents.insert(collisionInfo->fHitter); + DetectorLog("%s: Sending Volume Enter ActivatorMsg", GetKeyName().c_str()); + ISendTriggerMsg(collisionInfo->fHitter, true); + } + else + { + fCurrentResidents.erase(j); + DetectorLog("%s: Sending Volume Exit ActivatorMsg", GetKeyName().c_str()); + ISendTriggerMsg(collisionInfo->fHitter, false); + } + } + + delete collisionInfo; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + it = fCollisionList.erase(it); + } + else + { + ++it; + } +#else + ++it; +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + } +} + void plObjectInVolumeDetector::SetTarget(plSceneObject* so) { plCollisionDetector::SetTarget(so); @@ -485,13 +514,13 @@ void plObjectInVolumeAndFacingDetector::ICheckForTrigger() { DetectorLog("%s: Trigger InVolume&Facing", GetKeyName().c_str()); fTriggered = true; - ITrigger(avatar->GetKey(), true, true); + ISendTriggerMsg(avatar->GetKey(), true); } else if (!facing && fTriggered) { DetectorLog("%s: Untrigger InVolume&Facing", GetKeyName().c_str()); fTriggered = false; - ITrigger(avatar->GetKey(), false, true); + ISendTriggerMsg(avatar->GetKey(), false); } } } @@ -525,7 +554,7 @@ bool plObjectInVolumeAndFacingDetector::MsgReceive(plMessage* msg) if (fTriggered) { fTriggered = false; - ITrigger(plNetClientApp::GetInstance()->GetLocalPlayerKey(), false, true); + ISendTriggerMsg(plNetClientApp::GetInstance()->GetLocalPlayerKey(), false); } } diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h index d350341f..237aa692 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h +++ b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h @@ -53,6 +53,8 @@ class plArmatureMod; class plActivatorMsg; class plEvalMsg; +#define USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + class plCollisionDetector : public plDetectorModifier { protected: @@ -92,23 +94,37 @@ public: class plObjectInVolumeDetector : public plCollisionDetector { protected: - virtual void ITrigger(plKey hitter, bool entering, bool immediate=false); - virtual void ISendSavedTriggerMsgs(); - - plActivatorMsg* fSavedActivatorMsg; - uint32_t fNumEvals; - uint32_t fLastEnterEval; - uint32_t fLastExitEval; + class plCollisionBookKeepingInfo + { + public: + plCollisionBookKeepingInfo(const plKey& key, bool entering) + : fHitter(key), fEntering(entering) { } + + plKey fHitter; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + uint32_t fLastStep; +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + bool fEntering; + }; + + void ITrigger(plKey hitter, bool entering); + void ISendTriggerMsg(plKey hitter, bool entering); + void IRegisterForEval(); + virtual void IHandleEval(plEvalMsg*); + bool fWaitingForEval; + + typedef std::list bookKeepingList; + bookKeepingList fCollisionList; + typedef std::set ResidentSet; + ResidentSet fCurrentResidents; public: plObjectInVolumeDetector() - : plCollisionDetector(), fSavedActivatorMsg(nil), fNumEvals(0), fLastEnterEval(0), fLastExitEval(0) - { } + : plCollisionDetector(), fWaitingForEval(false) { } plObjectInVolumeDetector(int8_t type) - : plCollisionDetector(type), fSavedActivatorMsg(nil), fNumEvals(0), fLastEnterEval(0), fLastExitEval(0) - { } + : plCollisionDetector(type), fWaitingForEval(false) { } virtual ~plObjectInVolumeDetector() { } @@ -159,16 +175,17 @@ protected: typedef std::vector plCameraMsgVec; plCameraMsgVec fMessages; - bool fIsInside; - bool fSavingSendMsg; - bool fSavedMsgEnterFlag; - - virtual void ITrigger(plKey hitter, bool entering, bool immediate=false); - virtual void ISendSavedTriggerMsgs(); +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + uint32_t fLastStep; +#endif + bool fIsInside; + bool fEntering; + + void ISendTriggerMsg(); + virtual void IHandleEval(plEvalMsg*); public: plCameraRegionDetector() - : plObjectInVolumeDetector(), fIsInside(false), fSavingSendMsg(false) - { } + : plObjectInVolumeDetector(), fIsInside(false) { } ~plCameraRegionDetector(); virtual bool MsgReceive(plMessage* msg); From 155e329feca67620d788ef830671c8a6774d993a Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 21 Dec 2012 23:11:59 -0500 Subject: [PATCH 15/15] Include a comment that explains the humanness check Following a suggestion from @branan, I've added a comment that points out that the controller makes a different sized kinematic actor if the armature is human (male or female) --- Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index cf0d2cdf..c1b5cbe1 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -1993,7 +1993,11 @@ bool plArmatureMod::ValidatePhysics() return false; if (!fController) - fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, fPhysWidth, (fBodyType == kBoneBaseMale || fBodyType == kBoneBaseFemale)); + { + // The kinematic actor is made taller if the avatar is human (male or female) + fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, + fPhysWidth, (fBodyType == kBoneBaseMale || fBodyType == kBoneBaseFemale)); + } if (fController) {