/*==LICENSE==*
CyanWorlds . com Engine - MMOG client , server and tools
Copyright ( C ) 2011 Cyan Worlds , Inc .
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program , or any covered work , by linking or
combining it with any of RAD Game Tools Bink SDK , Autodesk 3 ds 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 , 3 ds Max EULA ,
PhysX SDK EULA , DirectX SDK EULA , OpenSSL and SSLeay licenses , IJG
JPEG Library README , Windows Media SDK EULA , or QuickTime SDK EULA , the
licensors of this Program grant you additional
permission to convey the resulting work . Corresponding Source for a
non - source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work .
You can contact Cyan Worlds , Inc . by email legal @ cyan . com
or by snail mail at :
Cyan Worlds , Inc .
14617 N Newport Hwy
Mead , WA 99021
* = = LICENSE = = */
# include "plPXPhysicalControllerCore.h"
# include "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"
# include "plPhysical/plPhysicalProxy.h"
# include "pnMessage/plSetNetGroupIDMsg.h"
# include "plMessage/plCollideMsg.h"
# include "plModifier/plDetectorLog.h"
//#include "NxVecExtendedVec3.h"
# include "NxPhysics.h"
# include "ControllerManager.h"
# include "NxCapsuleController.h"
# include "NxCapsuleShape.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
# ifndef PLASMA_EXTERNAL_RELEASE
hsBool plPXPhysicalControllerCore : : fDebugDisplay = false ;
# endif // PLASMA_EXTERNAL_RELEASE
int plPXPhysicalControllerCore : : fPXControllersMax = 0 ;
static ControllerManager gControllerMgr ;
static std : : vector < plPXPhysicalControllerCore * > gControllers ;
static bool gRebuildCache = false ;
class PXControllerHitReportWalk : public NxUserControllerHitReport
{
public :
virtual NxControllerAction onShapeHit ( const NxControllerShapeHit & hit )
{
plPXPhysicalControllerCore * ac = plPXPhysicalControllerCore : : FindController ( hit . controller ) ;
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 ) ;
# 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 ( ( hit . worldPos . z - bottomOfTheCapsule . fZ ) < = ac - > fRadius ) //bottom hemisphere
{
// If this is an animated physical, we can stand on it
if ( phys - > GetProperty ( plSimulationInterface : : kPhysAnim ) )
{
if ( normal . fZ > = 0 )
{
//we consider this ground
ac - > fMovementInterface - > AddOnTopOfObject ( 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
{
return NX_ACTION_NONE ;
}
if ( phys - > GetProperty ( plSimulationInterface : : kAvAnimPushable ) )
{
hsQuat inverseRotation = ac - > fLocalRotation . Inverse ( ) ;
hsVector3 normal = plPXConvert : : Vector ( hit . worldNormal ) ;
ac - > SetPushingPhysical ( phys ) ;
ac - > SetFacingPushingPhysical ( ( inverseRotation . Rotate ( & kAvatarForward ) . InnerProduct ( normal ) < 0 ? true : false ) ) ;
}
return NX_ACTION_NONE ;
}
virtual NxControllerAction onControllerHit ( const NxControllersHit & hit )
{
return NX_ACTION_NONE ;
}
} gMyReport ;
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 + + )
{
plPXPhysicalControllerCore * ac = gControllers [ i ] ;
if ( ac - > fController = = controller )
return ac ;
}
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 ; j < gControllers . size ( ) ; j + + )
{
plPXPhysicalControllerCore * ac = gControllers [ i ] ;
if ( ac - > GetSubworld ( ) = = world )
{
if ( i < maxToReturn )
{
bufferout [ i ] = ac ;
i + + ;
}
}
}
return i ;
}
int plPXPhysicalControllerCore : : GetNumberOfControllersInThisSubWorld ( plKey world )
{
int i = 0 ;
for ( int j = 0 ; j < gControllers . size ( ) ; j + + )
{
plPXPhysicalControllerCore * ac = gControllers [ i ] ;
if ( ac - > GetSubworld ( ) = = 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 ) ;
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 + + )
{
if ( gControllers [ i ] = = this )
{
gControllers . erase ( gControllers . begin ( ) + i ) ;
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 ) ;
// 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 = 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 ;
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
{
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 - > moveGlobalPose ( 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 : : 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 " ) ;
}
// 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 ) ;
}
}
void plPXPhysicalControllerCore : : IInformDetectors ( bool entering , bool deferUntilNextSim = true )
{
static const NxU32 DetectorFlag = 1 < < plSimDefs : : kGroupDetector ;
if ( fController )
{
# ifndef PLASMA_EXTERNAL_RELEASE
DetectorLog ( " Informing from plPXPhysicalControllerCore::IInformDetectors " ) ;
# endif
NxScene * scene = plSimulationMgr : : GetInstance ( ) - > 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 ; i < numCollided ; i + + )
{
NxActor * myactor = & ( shapes [ i ] - > getActor ( ) ) ;
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 )
fEnableChanged = true ;
else
{
// See ISendUpdates for why we don't re-enable right away
fController - > setCollision ( fEnabled ) ;
}
}
}
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! " ) ;
IDeleteController ( ) ;
SimLog ( " Deleted old controller " ) ;
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 ;
l2s . GetTranslate ( & fLocalPosition ) ;
fLocalRotation . SetFromMatrix44 ( l2s ) ;
}
else
{
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 ( ) ;
}
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
pos = fLocalPosition ;
}
void plPXPhysicalControllerCore : : SetState ( const hsPoint3 & pos , float zRot )
{
plSceneObject * so = plSceneObject : : ConvertNoRef ( fOwner - > ObjectIsLoaded ( ) ) ;
if ( so )
{
hsQuat worldRot ;
hsVector3 zAxis ( 0.f , 0.f , 1.f ) ;
worldRot . SetAngleAxis ( zRot , zAxis ) ;
hsMatrix44 l2w , w2l ;
worldRot . MakeMatrix ( & l2w ) ;
l2w . SetTranslate ( & pos ) ;
// Localize new position and rotation to global coords if we're in a subworld
const plCoordinateInterface * ci = GetSubworldCI ( ) ;
if ( ci )
{
const hsMatrix44 & subworldL2W = ci - > GetLocalToWorld ( ) ;
l2w = subworldL2W * l2w ;
}
l2w . GetInverse ( & w2l ) ;
so - > SetTransform ( l2w , w2l ) ;
so - > FlushTransform ( ) ;
}
}
// kinematic stuff .... should be just for when playing a behavior...
void plPXPhysicalControllerCore : : 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 plPXPhysicalControllerCore : : IsKinematic ( )
{
return fKinematic ;
}
void plPXPhysicalControllerCore : : GetKinematicPosition ( hsPoint3 & pos )
{
pos . Set ( - 1 , - 1 , - 1 ) ;
if ( fKinematicActor )
{
NxVec3 klPos = fKinematicActor - > getGlobalPosition ( ) ;
pos . Set ( float ( klPos . x ) , float ( klPos . y ) , float ( klPos . z ) - kPhysZOffset ) ;
}
}
void plPXPhysicalControllerCore : : UpdatePoststep ( float 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 + + )
{
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 ( ) ;
}
}
}
void plPXPhysicalControllerCore : : UpdatePrestep ( float delSecs )
{
for ( int i = 0 ; i < gControllers . size ( ) ; i + + )
{
plPXPhysicalControllerCore * ac = gControllers [ i ] ;
hsAssert ( ac , " Bad avatar controller " ) ;
//FIXME
# ifndef PLASMA_EXTERNAL_RELEASE
ac - > fDbgCollisionInfo . SetCount ( 0 ) ;
# endif // PLASMA_EXTERNAL_RELEASE
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 ;
}
void plPXPhysicalControllerCore : : UpdatePostSimStep ( float delSecs )
{
for ( int i = 0 ; i < gControllers . size ( ) ; i + + )
{
plPXPhysicalControllerCore * ac = gControllers [ i ] ;
hsAssert ( ac , " Bad avatar controller " ) ;
ac - > PostStep ( delSecs ) ;
}
}
void plPXPhysicalControllerCore : : HandleEnableChanged ( )
{
fEnableChanged = false ;
if ( this - > fBehavingLikeAnimatedPhys )
{
fController - > setCollision ( fEnabled ) ;
}
else
{
fController - > setCollision ( false ) ;
}
# 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);
}
void plPXPhysicalControllerCore : : HandleKinematicChanged ( )
{
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
}
void plPXPhysicalControllerCore : : HandleKinematicEnableNextUpdate ( )
{
fKinematicActor - > clearActorFlag ( NX_AF_DISABLE_COLLISION ) ;
fKinematicEnableNextUpdate = false ;
}
void plPXPhysicalControllerCore : : IHandleResize ( )
{
uint32_t collideFlags =
1 < < plSimDefs : : kGroupStatic |
1 < < plSimDefs : : kGroupAvatarBlocker |
1 < < plSimDefs : : kGroupDynamic ;
if ( ! IsSeeking ( ) )
{
collideFlags | = ( 1 < < plSimDefs : : kGroupExcludeRegion ) ;
}
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 )
{
fHeight = fPreferedHeight ;
fRadius = fPreferedRadius ;
fController - > setRadius ( fRadius ) ;
fController - > setHeight ( fHeight ) ;
fNeedsResize = false ;
}
// delete[] response;
}
void plPXPhysicalControllerCore : : SetControllerDimensions ( float radius , float height )
{
fNeedsResize = false ;
if ( fRadius ! = radius )
{
fNeedsResize = true ;
}
if ( fHeight ! = height )
{
fNeedsResize = true ;
}
fPreferedRadius = radius ;
fPreferedHeight = height ;
}
void plPXPhysicalControllerCore : : LeaveAge ( )
{
SetPushingPhysical ( nil ) ;
if ( fWorldKey ) this - > SetSubworld ( nil ) ;
this - > fMovementInterface - > LeaveAge ( ) ;
}
int plPXPhysicalControllerCore : : SweepControllerPath ( const hsPoint3 & startPos , const hsPoint3 & endPos , hsBool vsDynamics , hsBool vsStatics ,
uint32_t & vsSimGroups , std : : multiset < plControllerSweepRecord > & WhatWasHitOut )
{
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 ;
NxVec3 vec ;
vec . x = endPos . fX - startPos . fX ;
vec . y = endPos . fY - startPos . fY ;
vec . z = endPos . fZ - startPos . fZ ;
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 ; i < numberofHits ; i + + )
{
plControllerSweepRecord CurrentHit ;
CurrentHit . ObjHit = ( plPhysical * ) whatdidIhit [ i ] . hitShape - > getActor ( ) . 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 + + ;
}
}
}
return HitsReturned ;
}
void plPXPhysicalControllerCore : : BehaveLikeAnimatedPhysical ( hsBool actLikeAnAnimatedPhys )
{
hsAssert ( fKinematicActor , " Changing behavior, but plPXPhysicalControllerCore has no Kinematic actor associated with it " ) ;
if ( fBehavingLikeAnimatedPhys ! = actLikeAnAnimatedPhys )
{
fBehavingLikeAnimatedPhys = actLikeAnAnimatedPhys ;
if ( fKinematicActor )
{
if ( actLikeAnAnimatedPhys )
{
//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 ) ;
}
}
}
}
hsBool plPXPhysicalControllerCore : : BehavingLikeAnAnimatedPhysical ( )
{
hsAssert ( fKinematicActor , " plPXPhysicalControllerCore is missing a kinematic actor " ) ;
return fBehavingLikeAnimatedPhys ;
}
void plPXPhysicalControllerCore : : SetLinearVelocity ( const hsVector3 & linearVel )
{
plPhysicalControllerCore : : SetLinearVelocity ( linearVel ) ;
if ( fKinematicActor & & ! fBehavingLikeAnimatedPhys )
{
NxVec3 vel = plPXConvert : : Vector ( linearVel ) ;
fKinematicActor - > setLinearVelocity ( vel ) ;
}
}
void plPXPhysicalControllerCore : : SetAngularVelocity ( const float angvel )
{
plPhysicalControllerCore : : SetAngularVelocity ( angvel ) ;
if ( fKinematicActor & & ! fBehavingLikeAnimatedPhys )
{
NxVec3 vel ( 0.0f , 0.0f , angvel ) ;
fKinematicActor - > setAngularVelocity ( vel ) ;
}
}
void plPXPhysicalControllerCore : : SetVelocities ( const hsVector3 & linearVel , float angVel )
{
SetLinearVelocity ( linearVel ) ;
SetAngularVelocity ( angVel ) ;
}
void plPXPhysicalControllerCore : : IMatchControllerToKinematic ( )
{
NxExtendedVec3 newpos ;
NxVec3 pos = fKinematicActor - > getGlobalPosition ( ) ;
newpos . x = pos . x ;
newpos . y = pos . y ;
newpos . z = pos . z ;
fController - > setPosition ( newpos ) ;
}
const hsVector3 & plPXPhysicalControllerCore : : GetLinearVelocity ( )
{
if ( BehavingLikeAnAnimatedPhysical ( ) )
return fLinearVelocity ;
else
{
fLinearVelocity = plPXConvert : : Vector ( fKinematicActor - > getLinearVelocity ( ) ) ;
return fLinearVelocity ;
}
}
// Make a visible object that can be viewed by users for debugging purposes.
plDrawableSpans * plPXPhysicalControllerCore : : CreateProxy ( hsGMaterial * mat , hsTArray < uint32_t > & idx , plDrawableSpans * addTo )
{
plDrawableSpans * myDraw = addTo ;
hsBool 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 )
{
hsBool 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 plPXPhysicalControllerCore : : 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 " ;
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 ( ) ,
normal . fX , normal . fY , normal . fZ , angle , overlapStr ) ;
debugTxt . DrawString ( x , y , strBuf ) ;
y + = lineHeight ;
}
}
# endif PLASMA_EXTERNAL_RELEASE