/*==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 "plPXPhysical.h"
# include "NxPhysics.h"
# include "NxCooking.h"
# include "hsResMgr.h"
# include "hsStream.h"
# include "hsTimer.h"
# include "plProfile.h"
# include "hsQuat.h"
# include "hsSTLStream.h"
# include "plSimulationMgr.h"
# include "../plPhysical/plPhysicalSDLModifier.h"
# include "../plPhysical/plPhysicalSndGroup.h"
# include "../plPhysical/plPhysicalProxy.h"
# include "../pnSceneObject/plSimulationInterface.h"
# include "../pnSceneObject/plCoordinateInterface.h"
# include "../pnKeyedObject/plKey.h"
# include "../pnMessage/plCorrectionMsg.h"
# include "../pnMessage/plNodeRefMsg.h"
# include "../pnMessage/plSDLModifierMsg.h"
# include "../plMessage/plSimStateMsg.h"
# include "../plMessage/plSimInfluenceMsg.h"
# include "../plMessage/plLinearVelocityMsg.h"
# include "../plMessage/plAngularVelocityMsg.h"
# include "../plDrawable/plDrawableGenerator.h"
# include "../plNetClient/plNetClientMgr.h"
# include "../plNetTransport/plNetTransportMember.h"
# include "../plStatusLog/plStatusLog.h"
# include "plPXConvert.h"
# include "plPXPhysicalControllerCore.h"
# include "../plModifier/plDetectorLog.h"
#if 0
# define SpamMsg(x) x
# else
# define SpamMsg(x)
# endif
# define LogActivate(func) if (fActor->isSleeping()) SimLog("%s activated by %s", GetKeyName(), func);
PhysRecipe : : PhysRecipe ( )
: mass ( 0.f )
, friction ( 0.f )
, restitution ( 0.f )
, bounds ( plSimDefs : : kBoundsMax )
, group ( plSimDefs : : kGroupMax )
, reportsOn ( 0 )
, objectKey ( nil )
, sceneNode ( nil )
, worldKey ( nil )
, convexMesh ( nil )
, triMesh ( nil )
, radius ( 0.f )
, offset ( 0.f , 0.f , 0.f )
, meshStream ( nil )
{
l2s . Reset ( ) ;
}
plProfile_Extern ( MaySendLocation ) ;
plProfile_Extern ( LocationsSent ) ;
plProfile_Extern ( PhysicsUpdates ) ;
static void ClearMatrix ( hsMatrix44 & m )
{
m . fMap [ 0 ] [ 0 ] = 0.0f ; m . fMap [ 0 ] [ 1 ] = 0.0f ; m . fMap [ 0 ] [ 2 ] = 0.0f ; m . fMap [ 0 ] [ 3 ] = 0.0f ;
m . fMap [ 1 ] [ 0 ] = 0.0f ; m . fMap [ 1 ] [ 1 ] = 0.0f ; m . fMap [ 1 ] [ 2 ] = 0.0f ; m . fMap [ 1 ] [ 3 ] = 0.0f ;
m . fMap [ 2 ] [ 0 ] = 0.0f ; m . fMap [ 2 ] [ 1 ] = 0.0f ; m . fMap [ 2 ] [ 2 ] = 0.0f ; m . fMap [ 2 ] [ 3 ] = 0.0f ;
m . fMap [ 3 ] [ 0 ] = 0.0f ; m . fMap [ 3 ] [ 1 ] = 0.0f ; m . fMap [ 3 ] [ 2 ] = 0.0f ; m . fMap [ 3 ] [ 3 ] = 0.0f ;
m . NotIdentity ( ) ;
}
int plPXPhysical : : fNumberAnimatedPhysicals = 0 ;
int plPXPhysical : : fNumberAnimatedActivators = 0 ;
/////////////////////////////////////////////////////////////////
//
// plPXPhysical IMPLEMENTATION
//
/////////////////////////////////////////////////////////////////
plPXPhysical : : plPXPhysical ( )
: fSDLMod ( nil )
, fActor ( nil )
, fBoundsType ( plSimDefs : : kBoundsMax )
, fLOSDBs ( plSimDefs : : kLOSDBNone )
, fGroup ( plSimDefs : : kGroupMax )
, fReportsOn ( 0 )
, fLastSyncTime ( 0.0f )
, fProxyGen ( nil )
, 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 )
{
}
plPXPhysical : : ~ plPXPhysical ( )
{
SpamMsg ( plSimulationMgr : : Log ( " Destroying physical %s " , GetKeyName ( ) ) ) ;
if ( fActor )
{
// Grab any mesh we may have (they need to be released manually)
NxConvexMesh * convexMesh = nil ;
NxTriangleMesh * triMesh = nil ;
NxShape * shape = fActor - > getShapes ( ) [ 0 ] ;
if ( NxConvexShape * convexShape = shape - > isConvexMesh ( ) )
convexMesh = & convexShape - > getConvexMesh ( ) ;
else if ( NxTriangleMeshShape * trimeshShape = shape - > isTriangleMesh ( ) )
triMesh = & trimeshShape - > getTriangleMesh ( ) ;
if ( ! fActor - > isDynamic ( ) )
plPXPhysicalControllerCore : : RebuildCache ( ) ;
if ( fActor - > isDynamic ( ) & & fActor - > readBodyFlag ( NX_BF_KINEMATIC ) )
{
if ( fGroup = = plSimDefs : : kGroupDynamic )
fNumberAnimatedPhysicals - - ;
else
fNumberAnimatedActivators - - ;
}
// Release the actor
NxScene * scene = plSimulationMgr : : GetInstance ( ) - > GetScene ( fWorldKey ) ;
scene - > releaseActor ( * fActor ) ;
fActor = nil ;
// Now that the actor is freed, release the mesh
if ( convexMesh )
plSimulationMgr : : GetInstance ( ) - > GetSDK ( ) - > releaseConvexMesh ( * convexMesh ) ;
if ( triMesh )
plSimulationMgr : : GetInstance ( ) - > GetSDK ( ) - > releaseTriangleMesh ( * triMesh ) ;
// Release the scene, so it can be cleaned up if no one else is using it
plSimulationMgr : : GetInstance ( ) - > ReleaseScene ( fWorldKey ) ;
}
if ( fWorldHull )
delete [ ] fWorldHull ;
if ( fSaveTriangles )
delete [ ] fSaveTriangles ;
delete fProxyGen ;
// remove sdl modifier
plSceneObject * sceneObj = plSceneObject : : ConvertNoRef ( fObjectKey - > ObjectIsLoaded ( ) ) ;
if ( sceneObj & & fSDLMod )
{
sceneObj - > RemoveModifier ( fSDLMod ) ;
}
delete fSDLMod ;
}
static void MakeBoxFromHull ( NxConvexMesh * convexMesh , NxBoxShapeDesc & box )
{
NxConvexMeshDesc desc ;
convexMesh - > saveToDesc ( desc ) ;
hsScalar 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 = TRACKED_NEW hsPoint3 [ fHullNumberPlanes * 3 ] ;
for ( int i = 0 ; i < desc . numTriangles ; i + + )
{
UInt32 * triangle = ( UInt32 * ) ( ( ( 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 = TRACKED_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 ;
}
}
}
hsBool 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 ;
}
hsBool plPXPhysical : : Should_I_Trigger ( hsBool 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 ( ) ) ;
# else
DetectorLogSpecial ( " **>Could have saved a missing enter collision: %s " , GetObjectKey ( ) - > GetName ( ) ) ;
# endif PHYSX_SAVE_TRIGGERS_WORKAROUND
}
}
return trigger ;
}
hsBool plPXPhysical : : Init ( PhysRecipe & recipe )
{
hsBool startAsleep = false ;
fBoundsType = recipe . bounds ;
fGroup = recipe . group ;
fReportsOn = recipe . reportsOn ;
fObjectKey = recipe . objectKey ;
fSceneNode = recipe . sceneNode ;
fWorldKey = recipe . worldKey ;
NxActorDesc actorDesc ;
NxSphereShapeDesc sphereDesc ;
NxConvexShapeDesc convexShapeDesc ;
NxTriangleMeshShapeDesc trimeshShapeDesc ;
NxBoxShapeDesc boxDesc ;
plPXConvert : : Matrix ( recipe . l2s , actorDesc . globalPose ) ;
switch ( fBoundsType )
{
case plSimDefs : : kSphereBounds :
{
hsMatrix44 sphereL2W ;
sphereL2W . Reset ( ) ;
sphereL2W . SetTranslate ( & recipe . offset ) ;
sphereDesc . radius = recipe . radius ;
plPXConvert : : Matrix ( sphereL2W , sphereDesc . localPose ) ;
sphereDesc . group = fGroup ;
actorDesc . shapes . pushBack ( & sphereDesc ) ;
}
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 ;
convexShapeDesc . group = fGroup ;
actorDesc . shapes . pushBack ( & convexShapeDesc ) ;
}
break ;
case plSimDefs : : kBoxBounds :
{
boxDesc . dimensions = plPXConvert : : Point ( recipe . bDimensions ) ;
hsMatrix44 boxL2W ;
boxL2W . Reset ( ) ;
boxL2W . SetTranslate ( & recipe . bOffset ) ;
plPXConvert : : Matrix ( boxL2W , boxDesc . localPose ) ;
boxDesc . group = fGroup ;
actorDesc . shapes . push_back ( & boxDesc ) ;
}
break ;
case plSimDefs : : kExplicitBounds :
case plSimDefs : : kProxyBounds :
if ( fGroup = = plSimDefs : : kGroupDetector )
{
SimLog ( " Someone using an Exact on a detector region: %s " , GetKeyName ( ) ) ;
}
trimeshShapeDesc . meshData = recipe . triMesh ;
trimeshShapeDesc . userData = recipe . meshStream ;
trimeshShapeDesc . group = fGroup ;
actorDesc . shapes . pushBack ( & trimeshShapeDesc ) ;
break ;
default :
hsAssert ( false , " Unknown geometry type during read. " ) ;
return false ;
break ;
}
// Now fill out the body, or dynamic part of the physical
NxBodyDesc bodyDesc ;
fMass = recipe . mass ;
if ( recipe . mass ! = 0 )
{
bodyDesc . mass = recipe . mass ;
actorDesc . body = & bodyDesc ;
if ( GetProperty ( plSimulationInterface : : kPinned ) )
{
bodyDesc . flags | = NX_BF_FROZEN ;
startAsleep = true ; // put it to sleep if they are going to be frozen
}
if ( fGroup ! = plSimDefs : : kGroupDynamic | | GetProperty ( plSimulationInterface : : kPhysAnim ) )
{
SetProperty ( plSimulationInterface : : kPassive , true ) ;
// Even though the code for animated physicals and animated activators are the same
// keep these code snippets separated for fine tuning. Thanks.
if ( fGroup = = plSimDefs : : kGroupDynamic )
{
// handle the animated physicals.... make kinematic for now.
fNumberAnimatedPhysicals + + ;
bodyDesc . flags | = NX_BF_KINEMATIC ;
startAsleep = true ;
}
else
{
// handle the animated activators....
fNumberAnimatedActivators + + ;
bodyDesc . flags | = NX_BF_KINEMATIC ;
startAsleep = true ;
}
}
}
else
{
if ( GetProperty ( plSimulationInterface : : kPhysAnim ) )
SimLog ( " An animated physical that has no mass: %s " , GetKeyName ( ) ) ;
}
actorDesc . userData = this ;
actorDesc . name = GetKeyName ( ) ;
// Put the dynamics into actor group 1. The actor groups are only used for
// deciding who we get contact reports for.
if ( fGroup = = plSimDefs : : kGroupDynamic )
actorDesc . group = 1 ;
NxScene * scene = plSimulationMgr : : GetInstance ( ) - > GetScene ( fWorldKey ) ;
try
{
fActor = scene - > createActor ( actorDesc ) ;
} catch ( . . . )
{
hsAssert ( false , " Actor creation crashed " ) ;
return false ;
}
hsAssert ( fActor , " Actor creation failed " ) ;
if ( ! fActor )
return false ;
NxShape * shape = fActor - > getShapes ( ) [ 0 ] ;
shape - > setMaterial ( plSimulationMgr : : GetInstance ( ) - > GetMaterialIdx ( scene , recipe . friction , recipe . restitution ) ) ;
// Turn on the trigger flags for any detectors.
//
// Normally, we'd set these flags on the shape before it's created. However,
// in the case where the detector is going to be animated, it'll have a rigid
// body too, and that will cause problems at creation. According to Ageia,
// a detector shape doesn't actually count as a shape, so the SDK will have
// problems trying to calculate an intertial tensor. By letting it be
// created as a normal dynamic first, then setting the flags, we work around
// that problem.
if ( fGroup = = plSimDefs : : kGroupDetector )
{
shape - > setFlag ( NX_TRIGGER_ON_ENTER , true ) ;
shape - > setFlag ( NX_TRIGGER_ON_LEAVE , true ) ;
}
if ( GetProperty ( plSimulationInterface : : kStartInactive ) | | startAsleep )
{
if ( ! fActor - > isSleeping ( ) )
{
if ( plSimulationMgr : : fExtraProfile )
SimLog ( " Deactivating %s in SetPositionAndRotationSim " , GetKeyName ( ) ) ;
fActor - > putToSleep ( ) ;
}
}
if ( GetProperty ( plSimulationInterface : : kDisable ) )
IEnable ( false ) ;
if ( GetProperty ( plSimulationInterface : : kSuppressed_DEAD ) )
IEnable ( false ) ;
plNodeRefMsg * refMsg = TRACKED_NEW plNodeRefMsg ( fSceneNode , plRefMsg : : kOnCreate , - 1 , plNodeRefMsg : : kPhysical ) ;
hsgResMgr : : ResMgr ( ) - > AddViaNotify ( GetKey ( ) , refMsg , plRefFlags : : kActiveRef ) ;
if ( fWorldKey )
{
plGenRefMsg * ref = TRACKED_NEW plGenRefMsg ( GetKey ( ) , plRefMsg : : kOnCreate , 0 , kPhysRefWorld ) ;
hsgResMgr : : ResMgr ( ) - > AddViaNotify ( fWorldKey , ref , plRefFlags : : kActiveRef ) ;
}
// only dynamic physicals without noSync need SDLs
if ( fGroup = = plSimDefs : : kGroupDynamic & & ! fProps . IsBitSet ( plSimulationInterface : : kNoSynchronize ) )
{
// add SDL modifier
plSceneObject * sceneObj = plSceneObject : : ConvertNoRef ( fObjectKey - > ObjectIsLoaded ( ) ) ;
hsAssert ( sceneObj , " nil sceneObject, failed to create and attach SDL modifier " ) ;
delete fSDLMod ;
fSDLMod = TRACKED_NEW plPhysicalSDLModifier ;
sceneObj - > AddModifier ( fSDLMod ) ;
}
return true ;
}
/////////////////////////////////////////////////////////////////
//
// MESSAGE HANDLING
//
/////////////////////////////////////////////////////////////////
// MSGRECEIVE
hsBool plPXPhysical : : MsgReceive ( plMessage * msg )
{
if ( plGenRefMsg * refM = plGenRefMsg : : ConvertNoRef ( msg ) )
{
return HandleRefMsg ( refM ) ;
}
else if ( plSimulationMsg * simM = plSimulationMsg : : ConvertNoRef ( msg ) )
{
plLinearVelocityMsg * velMsg = plLinearVelocityMsg : : ConvertNoRef ( msg ) ;
if ( velMsg )
{
SetLinearVelocitySim ( velMsg - > Velocity ( ) ) ;
return true ;
}
plAngularVelocityMsg * angvelMsg = plAngularVelocityMsg : : ConvertNoRef ( msg ) ;
if ( angvelMsg )
{
SetAngularVelocitySim ( angvelMsg - > AngularVelocity ( ) ) ;
return true ;
}
return false ;
}
// couldn't find a local handler: pass to the base
else
return plPhysical : : MsgReceive ( msg ) ;
}
// IHANDLEREFMSG
// there's two things we hold references to: subworlds
// and the simulation manager.
// right now, we're only worrying about the subworlds
hsBool plPXPhysical : : HandleRefMsg ( plGenRefMsg * refMsg )
{
UInt8 refCtxt = refMsg - > GetContext ( ) ;
plKey refKey = refMsg - > GetRef ( ) - > GetKey ( ) ;
plKey ourKey = GetKey ( ) ;
PhysRefType refType = PhysRefType ( refMsg - > fType ) ;
const char * refKeyName = refKey ? refKey - > GetName ( ) : " MISSING " ;
if ( refType = = kPhysRefWorld )
{
if ( refCtxt = = plRefMsg : : kOnCreate | | refCtxt = = plRefMsg : : kOnRequest )
{
// Cache the initial transform, since we assume the sceneobject already knows
// that and doesn't need to be told again
IGetTransformGlobal ( fCachedLocal2World ) ;
}
if ( refCtxt = = plRefMsg : : kOnDestroy )
{
// our world was deleted out from under us: move to the main world
// hsAssert(0, "Lost world");
}
}
else if ( refType = = kPhysRefSndGroup )
{
switch ( refCtxt )
{
case plRefMsg : : kOnCreate :
case plRefMsg : : kOnRequest :
fSndGroup = plPhysicalSndGroup : : ConvertNoRef ( refMsg - > GetRef ( ) ) ;
break ;
case plRefMsg : : kOnDestroy :
fSndGroup = nil ;
break ;
}
}
else
{
hsAssert ( 0 , " Unknown ref type, who sent us this? " ) ;
}
return true ;
}
void plPXPhysical : : IEnable ( hsBool enable )
{
fProps . SetBit ( plSimulationInterface : : kDisable , ! enable ) ;
if ( ! enable )
{
fActor - > raiseActorFlag ( NX_AF_DISABLE_COLLISION ) ;
if ( fActor - > isDynamic ( ) )
fActor - > raiseBodyFlag ( NX_BF_FROZEN ) ;
else
plPXPhysicalControllerCore : : RebuildCache ( ) ;
}
else
{
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
plPXPhysicalControllerCore : : RebuildCache ( ) ;
}
}
plPhysical & plPXPhysical : : SetProperty ( int prop , hsBool status )
{
if ( GetProperty ( prop ) = = status )
{
const char * propName = " (unknown) " ;
switch ( prop )
{
case plSimulationInterface : : kDisable : propName = " kDisable " ; break ;
case plSimulationInterface : : kPinned : propName = " kPinned " ; break ;
case plSimulationInterface : : kPassive : propName = " kPassive " ; break ;
case plSimulationInterface : : kPhysAnim : propName = " kPhysAnim " ; break ;
case plSimulationInterface : : kStartInactive : propName = " kStartInactive " ; break ;
case plSimulationInterface : : kNoSynchronize : propName = " kNoSynchronize " ; break ;
}
const char * name = " (unknown) " ;
if ( GetKey ( ) )
name = GetKeyName ( ) ;
if ( plSimulationMgr : : fExtraProfile )
plSimulationMgr : : Log ( " Warning: Redundant physical property set (property %s, value %s) on %s " , propName , status ? " true " : " false " , name ) ;
}
switch ( prop )
{
case plSimulationInterface : : kDisable :
IEnable ( ! status ) ;
break ;
case plSimulationInterface : : kPinned :
if ( fActor - > isDynamic ( ) )
{
// if the body is already unpinned and you unpin it again,
// you'll wipe out its velocity. hence the check.
hsBool current = fActor - > readBodyFlag ( NX_BF_FROZEN ) ;
if ( status ! = current )
{
if ( status )
fActor - > raiseBodyFlag ( NX_BF_FROZEN ) ;
else
{
fActor - > clearBodyFlag ( NX_BF_FROZEN ) ;
LogActivate ( " SetProperty " ) ;
fActor - > wakeUp ( ) ;
}
}
}
break ;
}
fProps . SetBit ( prop , status ) ;
return * this ;
}
plProfile_Extern ( SetTransforms ) ;
# define kMaxNegativeZPos -2000.f
bool CompareMatrices ( const hsMatrix44 & matA , const hsMatrix44 & matB , float tolerance )
{
return
( fabs ( matA . fMap [ 0 ] [ 0 ] - matB . fMap [ 0 ] [ 0 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 0 ] [ 1 ] - matB . fMap [ 0 ] [ 1 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 0 ] [ 2 ] - matB . fMap [ 0 ] [ 2 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 0 ] [ 3 ] - matB . fMap [ 0 ] [ 3 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 1 ] [ 0 ] - matB . fMap [ 1 ] [ 0 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 1 ] [ 1 ] - matB . fMap [ 1 ] [ 1 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 1 ] [ 2 ] - matB . fMap [ 1 ] [ 2 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 1 ] [ 3 ] - matB . fMap [ 1 ] [ 3 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 2 ] [ 0 ] - matB . fMap [ 2 ] [ 0 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 2 ] [ 1 ] - matB . fMap [ 2 ] [ 1 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 2 ] [ 2 ] - matB . fMap [ 2 ] [ 2 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 2 ] [ 3 ] - matB . fMap [ 2 ] [ 3 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 3 ] [ 0 ] - matB . fMap [ 3 ] [ 0 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 3 ] [ 1 ] - matB . fMap [ 3 ] [ 1 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 3 ] [ 2 ] - matB . fMap [ 3 ] [ 2 ] ) < tolerance ) & &
( fabs ( matA . fMap [ 3 ] [ 3 ] - matB . fMap [ 3 ] [ 3 ] ) < tolerance ) ;
}
// Called after the simulation has run....sends new positions to the various scene objects
// *** want to do this in response to an update message....
void plPXPhysical : : SendNewLocation ( hsBool synchTransform , hsBool isSynchUpdate )
{
// we only send if:
// - the body is active or forceUpdate is on
// - the mass is non-zero
// - the physical is not passive
hsBool bodyActive = ! fActor - > isSleeping ( ) ;
hsBool dynamic = fActor - > isDynamic ( ) ;
if ( ( bodyActive | | isSynchUpdate ) & & dynamic ) // && fInitialTransform)
{
plProfile_Inc ( MaySendLocation ) ;
if ( ! GetProperty ( plSimulationInterface : : kPassive ) )
{
hsMatrix44 curl2w = fCachedLocal2World ;
// we're going to cache the transform before sending so we can recognize if it comes back
IGetTransformGlobal ( fCachedLocal2World ) ;
if ( ! CompareMatrices ( curl2w , fCachedLocal2World , .0001f ) )
{
plProfile_Inc ( LocationsSent ) ;
plProfile_BeginLap ( PhysicsUpdates , GetKeyName ( ) ) ;
// quick peek at the translation...last time it was corrupted because we applied a non-unit quaternion
// hsAssert(real_finite(fCachedLocal2World.fMap[0][3]) &&
// real_finite(fCachedLocal2World.fMap[1][3]) &&
// real_finite(fCachedLocal2World.fMap[2][3]), "Bad transform outgoing");
if ( fCachedLocal2World . GetTranslate ( ) . fZ < kMaxNegativeZPos )
{
SimLog ( " Physical %s fell to %.1f (%.1f is the max). Suppressing. " , GetKeyName ( ) , fCachedLocal2World . GetTranslate ( ) . fZ , kMaxNegativeZPos ) ;
// Since this has probably been falling for a while, and thus not getting any syncs,
// make sure to save it's current pos so we'll know to reset it later
DirtySynchState ( kSDLPhysical , plSynchedObject : : kBCastToClients ) ;
IEnable ( false ) ;
}
hsMatrix44 w2l ;
fCachedLocal2World . GetInverse ( & w2l ) ;
plCorrectionMsg * pCorrMsg = TRACKED_NEW plCorrectionMsg ( GetObjectKey ( ) , fCachedLocal2World , w2l , synchTransform ) ;
pCorrMsg - > Send ( ) ;
if ( fProxyGen )
fProxyGen - > SetTransform ( fCachedLocal2World , w2l ) ;
plProfile_EndLap ( PhysicsUpdates , GetKeyName ( ) ) ;
}
}
}
}
void plPXPhysical : : ApplyHitForce ( )
{
if ( fActor & & fWeWereHit )
{
fActor - > addForceAtPos ( plPXConvert : : Vector ( fHitForce ) , plPXConvert : : Point ( fHitPos ) , NX_FORCE ) ;
fWeWereHit = false ;
}
}
void plPXPhysical : : ISetTransformGlobal ( const hsMatrix44 & l2w )
{
hsAssert ( fActor - > isDynamic ( ) , " Shouldn't move a static actor " ) ;
NxMat34 mat ;
if ( fWorldKey )
{
plSceneObject * so = plSceneObject : : ConvertNoRef ( fWorldKey - > ObjectIsLoaded ( ) ) ;
hsAssert ( so , " Scene object not loaded while accessing subworld. " ) ;
// physical to subworld (simulation space)
hsMatrix44 p2s = so - > GetCoordinateInterface ( ) - > GetWorldToLocal ( ) * l2w ;
plPXConvert : : Matrix ( p2s , mat ) ;
if ( fProxyGen )
{
hsMatrix44 w2l ;
p2s . GetInverse ( & w2l ) ;
fProxyGen - > SetTransform ( p2s , w2l ) ;
}
}
// No need to localize
else
{
plPXConvert : : Matrix ( l2w , mat ) ;
if ( fProxyGen )
{
hsMatrix44 w2l ;
l2w . GetInverse ( & w2l ) ;
fProxyGen - > SetTransform ( l2w , w2l ) ;
}
}
if ( GetProperty ( plSimulationInterface : : kPhysAnim ) )
{ hsAssert ( fActor - > readBodyFlag ( NX_BF_KINEMATIC ) , " This Should be kinematic " ) ;
fActor - > moveGlobalPose ( mat ) ;
}
else
fActor - > setGlobalPose ( mat ) ;
}
// the physical may have several parents between it and the subworld object,
// but the *havok* transform is only one level away from the subworld.
// to avoid any confusion about this difference, we avoid referring to the
// subworld as "parent" and use, for example, "l2s" (local-to-sub) instead
// of the canonical plasma "l2p" (local-to-parent)
void plPXPhysical : : IGetTransformGlobal ( hsMatrix44 & l2w ) const
{
plPXConvert : : Matrix ( fActor - > getGlobalPose ( ) , l2w ) ;
if ( fWorldKey )
{
plSceneObject * so = plSceneObject : : ConvertNoRef ( fWorldKey - > ObjectIsLoaded ( ) ) ;
hsAssert ( so , " Scene object not loaded while accessing subworld. " ) ;
// We'll hit this at export time, when the ci isn't ready yet, so do a check
if ( so - > GetCoordinateInterface ( ) )
{
const hsMatrix44 & s2w = so - > GetCoordinateInterface ( ) - > GetLocalToWorld ( ) ;
l2w = s2w * l2w ;
}
}
}
void plPXPhysical : : IGetPositionSim ( hsPoint3 & pos ) const
{
pos = plPXConvert : : Point ( fActor - > getGlobalPosition ( ) ) ;
}
void plPXPhysical : : IGetRotationSim ( hsQuat & rot ) const
{
rot = plPXConvert : : Quat ( fActor - > getGlobalOrientationQuat ( ) ) ;
}
void plPXPhysical : : ISetPositionSim ( const hsPoint3 & pos )
{
if ( GetProperty ( plSimulationInterface : : kPhysAnim ) )
fActor - > moveGlobalPosition ( plPXConvert : : Point ( pos ) ) ;
else
fActor - > setGlobalPosition ( plPXConvert : : Point ( pos ) ) ;
}
void plPXPhysical : : ISetRotationSim ( const hsQuat & rot )
{
if ( GetProperty ( plSimulationInterface : : kPhysAnim ) )
fActor - > moveGlobalOrientation ( plPXConvert : : Quat ( rot ) ) ;
else
fActor - > setGlobalOrientation ( plPXConvert : : Quat ( rot ) ) ;
}
// This form is assumed by convention to be global.
void plPXPhysical : : SetTransform ( const hsMatrix44 & l2w , const hsMatrix44 & w2l , hsBool force )
{
// hsAssert(real_finite(l2w.fMap[0][3]) && real_finite(l2w.fMap[1][3]) && real_finite(l2w.fMap[2][3]), "Bad transform incoming");
// make sure the physical is dynamic.
// also make sure there is some difference between the matrices...
// ... but not when a subworld... because the subworld maybe animating and if the object is still then it is actually moving within the subworld
if ( force | | ( fActor - > isDynamic ( ) & & ( fWorldKey | | ! CompareMatrices ( l2w , fCachedLocal2World , .0001f ) ) ) )
{
ISetTransformGlobal ( l2w ) ;
plProfile_Inc ( SetTransforms ) ;
}
else
{
if ( ! fActor - > isDynamic ( ) & & plSimulationMgr : : fExtraProfile )
SimLog ( " Setting transform on non-dynamic: %s. " , GetKeyName ( ) ) ;
}
}
// GETTRANSFORM
void plPXPhysical : : GetTransform ( hsMatrix44 & l2w , hsMatrix44 & w2l )
{
IGetTransformGlobal ( l2w ) ;
l2w . GetInverse ( & w2l ) ;
}
hsBool plPXPhysical : : GetLinearVelocitySim ( hsVector3 & vel ) const
{
hsBool result = false ;
if ( fActor - > isDynamic ( ) )
{
vel = plPXConvert : : Vector ( fActor - > getLinearVelocity ( ) ) ;
result = true ;
}
else
vel . Set ( 0 , 0 , 0 ) ;
return result ;
}
void plPXPhysical : : SetLinearVelocitySim ( const hsVector3 & vel )
{
if ( fActor - > isDynamic ( ) )
fActor - > setLinearVelocity ( plPXConvert : : Vector ( vel ) ) ;
}
void plPXPhysical : : ClearLinearVelocity ( )
{
SetLinearVelocitySim ( hsVector3 ( 0 , 0 , 0 ) ) ;
}
hsBool plPXPhysical : : GetAngularVelocitySim ( hsVector3 & vel ) const
{
hsBool result = false ;
if ( fActor - > isDynamic ( ) )
{
vel = plPXConvert : : Vector ( fActor - > getAngularVelocity ( ) ) ;
result = true ;
}
else
vel . Set ( 0 , 0 , 0 ) ;
return result ;
}
void plPXPhysical : : SetAngularVelocitySim ( const hsVector3 & vel )
{
if ( fActor - > isDynamic ( ) )
fActor - > setAngularVelocity ( plPXConvert : : Vector ( vel ) ) ;
}
///////////////////////////////////////////////////////////////
//
// NETWORK SYNCHRONIZATION
//
///////////////////////////////////////////////////////////////
plKey plPXPhysical : : GetSceneNode ( ) const
{
return fSceneNode ;
}
void plPXPhysical : : SetSceneNode ( plKey newNode )
{
# ifdef HS_DEBUGGING
plKey oldNode = GetSceneNode ( ) ;
char msg [ 1024 ] ;
if ( newNode )
sprintf ( msg , " Physical object %s cannot change scenes. Already in %s, trying to switch to %s. " , fObjectKey - > GetName ( ) , oldNode - > GetName ( ) , newNode - > GetName ( ) ) ;
else
sprintf ( msg , " Physical object %s cannot change scenes. Already in %s, trying to switch to <nil key>. " , fObjectKey - > GetName ( ) , oldNode - > GetName ( ) ) ;
hsAssert ( oldNode = = newNode , msg ) ;
# endif // HS_DEBUGGING
}
/////////////////////////////////////////////////////////////////////
//
// READING AND WRITING
//
/////////////////////////////////////////////////////////////////////
# include "plPXStream.h"
void plPXPhysical : : Read ( hsStream * stream , hsResMgr * mgr )
{
plPhysical : : Read ( stream , mgr ) ;
ClearMatrix ( fCachedLocal2World ) ;
PhysRecipe recipe ;
recipe . mass = stream - > ReadSwapScalar ( ) ;
recipe . friction = stream - > ReadSwapScalar ( ) ;
recipe . restitution = stream - > ReadSwapScalar ( ) ;
recipe . bounds = ( plSimDefs : : Bounds ) stream - > ReadByte ( ) ;
recipe . group = ( plSimDefs : : Group ) stream - > ReadByte ( ) ;
recipe . reportsOn = stream - > ReadSwap32 ( ) ;
fLOSDBs = stream - > ReadSwap16 ( ) ;
//hack for swim regions currently they are labeled as static av blockers
if ( fLOSDBs = = plSimDefs : : kLOSDBSwimRegion )
{
recipe . group = plSimDefs : : kGroupMax ;
}
//
recipe . objectKey = mgr - > ReadKey ( stream ) ;
recipe . sceneNode = mgr - > ReadKey ( stream ) ;
recipe . worldKey = mgr - > ReadKey ( stream ) ;
mgr - > ReadKeyNotifyMe ( stream , TRACKED_NEW plGenRefMsg ( GetKey ( ) , plRefMsg : : kOnCreate , 0 , kPhysRefSndGroup ) , plRefFlags : : kActiveRef ) ;
hsPoint3 pos ;
hsQuat rot ;
pos . Read ( stream ) ;
rot . Read ( stream ) ;
rot . MakeMatrix ( & recipe . l2s ) ;
recipe . l2s . SetTranslate ( & pos ) ;
fProps . Read ( stream ) ;
if ( recipe . bounds = = plSimDefs : : kSphereBounds )
{
recipe . radius = stream - > ReadSwapScalar ( ) ;
recipe . offset . Read ( stream ) ;
}
else if ( recipe . bounds = = plSimDefs : : kBoxBounds )
{
recipe . bDimensions . Read ( stream ) ;
recipe . bOffset . Read ( stream ) ;
}
else
{
if ( recipe . bounds = = plSimDefs : : kHullBounds )
recipe . convexMesh = IReadHull ( stream ) ;
else
recipe . triMesh = IReadTriMesh ( stream ) ;
}
Init ( recipe ) ;
hsAssert ( ! fProxyGen , " Already have proxy gen, double read? " ) ;
hsColorRGBA physColor ;
hsScalar opac = 1.0f ;
if ( fGroup = = plSimDefs : : kGroupAvatar )
{
// local avatar is light purple and transparent
physColor . Set ( .2f , .1f , .2f , 1.f ) ;
opac = 0.4f ;
}
else if ( fGroup = = plSimDefs : : kGroupDynamic )
{
// Dynamics are red
physColor . Set ( 1.f , 0.f , 0.f , 1.f ) ;
}
else if ( fGroup = = plSimDefs : : kGroupDetector )
{
if ( ! fWorldKey )
{
// Detectors are blue, and transparent
physColor . Set ( 0.f , 0.f , 1.f , 1.f ) ;
opac = 0.3f ;
}
else
{
// subworld Detectors are green
physColor . Set ( 0.f , 1.f , 0.f , 1.f ) ;
opac = 0.3f ;
}
}
else if ( fGroup = = plSimDefs : : kGroupStatic )
{
if ( GetProperty ( plSimulationInterface : : kPhysAnim ) )
// Statics that are animated are more reddish?
physColor . Set ( 1.f , 0.6f , 0.2f , 1.f ) ;
else
// Statics are yellow
physColor . Set ( 1.f , 0.8f , 0.2f , 1.f ) ;
// if in a subworld... slightly transparent
if ( fWorldKey )
opac = 0.6f ;
}
else
{
// don't knows are grey
physColor . Set ( 0.6f , 0.6f , 0.6f , 1.f ) ;
}
fProxyGen = TRACKED_NEW plPhysicalProxy ( hsColorRGBA ( ) . Set ( 0 , 0 , 0 , 1.f ) , physColor , opac ) ;
fProxyGen - > Init ( this ) ;
}
static bool IIsNxStream ( hsStream * s )
{
char tag [ 4 ] ;
s - > Read ( sizeof ( tag ) , tag ) ;
// PhysX streams begin with the magic string "NXS\x01"
// Our hacked streams begin with the magic string "HSP\x01"
if ( memcmp ( " HSP \x01 " , tag , sizeof ( tag ) ) = = 0 )
return false ;
// We're not going to compare to see if it says NXS. We will let the PhysX SDK
// worry about garbage data. Just rewind the stream back so PhysX can deal with it.
s - > SetPosition ( s - > GetPosition ( ) - sizeof ( tag ) ) ;
return true ;
}
NxConvexMesh * plPXPhysical : : IReadHull ( hsStream * s )
{
if ( IIsNxStream ( s ) )
{
plPXStream pxs ( s ) ;
return plSimulationMgr : : GetInstance ( ) - > GetSDK ( ) - > createConvexMesh ( pxs ) ;
}
else
{
std : : vector < hsPoint3 > verts ;
verts . resize ( s - > ReadSwap32 ( ) ) ;
for ( size_t i = 0 ; i < verts . size ( ) ; + + i )
verts [ i ] . Read ( s ) ;
// Unfortunately, the only way I know of to accomplish this is to cook to a RAM stream,
// then have PhysX read the cooked data from the RAM stream. Yes, this is very sad.
// I blame PhysX. It needs to die in a fiaaaaaaaaaaah
hsRAMStream ram ;
plPXStream pxs ( & ram ) ;
NxConvexMeshDesc desc ;
desc . numVertices = verts . size ( ) ;
desc . pointStrideBytes = sizeof ( hsPoint3 ) ;
desc . points = & verts [ 0 ] ;
desc . flags = NX_CF_COMPUTE_CONVEX | NX_CF_USE_UNCOMPRESSED_NORMALS ;
if ( ! NxCookConvexMesh ( desc , pxs ) )
{
SimLog ( " Failed to cook hull for '%s' " , GetKeyName ( ) ) ;
return NULL ;
}
ram . Rewind ( ) ;
return plSimulationMgr : : GetInstance ( ) - > GetSDK ( ) - > createConvexMesh ( pxs ) ;
}
}
NxTriangleMesh * plPXPhysical : : IReadTriMesh ( hsStream * s )
{
if ( IIsNxStream ( s ) )
{
plPXStream pxs ( s ) ;
return plSimulationMgr : : GetInstance ( ) - > GetSDK ( ) - > createTriangleMesh ( pxs ) ;
}
else
{
std : : vector < hsPoint3 > verts ;
verts . resize ( s - > ReadSwap32 ( ) ) ;
for ( size_t i = 0 ; i < verts . size ( ) ; + + i )
verts [ i ] . Read ( s ) ;
std : : vector < UInt32 > indices ;
UInt32 numTriangles = s - > ReadSwap32 ( ) ;
indices . resize ( numTriangles * 3 ) ;
for ( size_t i = 0 ; i < indices . size ( ) ; + + i )
indices [ i ] = s - > ReadSwap32 ( ) ;
// Unfortunately, the only way I know of to accomplish this is to cook to a RAM stream,
// then have PhysX read the cooked data from the RAM stream. Yes, this is very sad.
// I blame PhysX. It needs to die in a fiaaaaaaaaaaah
hsRAMStream ram ;
plPXStream pxs ( & ram ) ;
NxTriangleMeshDesc desc ;
desc . numVertices = verts . size ( ) ;
desc . pointStrideBytes = sizeof ( hsPoint3 ) ;
desc . points = & verts [ 0 ] ;
desc . numTriangles = numTriangles ;
desc . triangleStrideBytes = sizeof ( UInt32 ) * 3 ;
desc . triangles = & indices [ 0 ] ;
desc . flags = 0 ;
if ( ! NxCookTriangleMesh ( desc , pxs ) )
{
SimLog ( " Failed to cook trimesh for '%s' " , GetKeyName ( ) ) ;
return NULL ;
}
ram . Rewind ( ) ;
return plSimulationMgr : : GetInstance ( ) - > GetSDK ( ) - > createTriangleMesh ( pxs ) ;
}
}
void plPXPhysical : : Write ( hsStream * stream , hsResMgr * mgr )
{
plPhysical : : Write ( stream , mgr ) ;
hsAssert ( fActor , " nil actor " ) ;
hsAssert ( fActor - > getNbShapes ( ) = = 1 , " Can only write actors with one shape. Writing first only. " ) ;
NxShape * shape = fActor - > getShapes ( ) [ 0 ] ;
NxMaterialIndex matIdx = shape - > getMaterial ( ) ;
NxScene * scene = plSimulationMgr : : GetInstance ( ) - > GetScene ( fWorldKey ) ;
NxMaterial * mat = scene - > getMaterialFromIndex ( matIdx ) ;
float friction = mat - > getStaticFriction ( ) ;
float restitution = mat - > getRestitution ( ) ;
stream - > WriteSwapScalar ( fActor - > getMass ( ) ) ;
stream - > WriteSwapScalar ( friction ) ;
stream - > WriteSwapScalar ( restitution ) ;
stream - > WriteByte ( fBoundsType ) ;
stream - > WriteByte ( fGroup ) ;
stream - > WriteSwap32 ( fReportsOn ) ;
stream - > WriteSwap16 ( fLOSDBs ) ;
mgr - > WriteKey ( stream , fObjectKey ) ;
mgr - > WriteKey ( stream , fSceneNode ) ;
mgr - > WriteKey ( stream , fWorldKey ) ;
mgr - > WriteKey ( stream , fSndGroup ) ;
hsPoint3 pos ;
hsQuat rot ;
IGetPositionSim ( pos ) ;
IGetRotationSim ( rot ) ;
pos . Write ( stream ) ;
rot . Write ( stream ) ;
fProps . Write ( stream ) ;
if ( fBoundsType = = plSimDefs : : kSphereBounds )
{
const NxSphereShape * sphereShape = shape - > isSphere ( ) ;
stream - > WriteSwapScalar ( sphereShape - > getRadius ( ) ) ;
hsPoint3 localPos = plPXConvert : : Point ( sphereShape - > getLocalPosition ( ) ) ;
localPos . Write ( stream ) ;
}
else if ( fBoundsType = = plSimDefs : : kBoxBounds )
{
const NxBoxShape * boxShape = shape - > isBox ( ) ;
hsPoint3 dim = plPXConvert : : Point ( boxShape - > getDimensions ( ) ) ;
dim . Write ( stream ) ;
hsPoint3 localPos = plPXConvert : : Point ( boxShape - > getLocalPosition ( ) ) ;
localPos . Write ( stream ) ;
}
else
{
if ( fBoundsType = = plSimDefs : : kHullBounds )
hsAssert ( shape - > isConvexMesh ( ) , " Hull shape isn't a convex mesh " ) ;
else
hsAssert ( shape - > isTriangleMesh ( ) , " Exact shape isn't a trimesh " ) ;
// We hide the stream we used to create this mesh away in the shape user data.
// Pull it out and write it to disk.
hsVectorStream * vecStream = ( hsVectorStream * ) shape - > userData ;
stream - > Write ( vecStream - > GetEOF ( ) , vecStream - > GetData ( ) ) ;
delete vecStream ;
}
}
//
// TESTING SDL
// Send phys sendState msg to object's plPhysicalSDLModifier
//
hsBool plPXPhysical : : DirtySynchState ( const char * SDLStateName , UInt32 synchFlags )
{
if ( GetObjectKey ( ) )
{
plSynchedObject * so = plSynchedObject : : ConvertNoRef ( GetObjectKey ( ) - > ObjectIsLoaded ( ) ) ;
if ( so )
{
fLastSyncTime = hsTimer : : GetSysSeconds ( ) ;
return so - > DirtySynchState ( SDLStateName , synchFlags ) ;
}
}
return false ;
}
void plPXPhysical : : GetSyncState ( hsPoint3 & pos , hsQuat & rot , hsVector3 & linV , hsVector3 & angV )
{
IGetPositionSim ( pos ) ;
IGetRotationSim ( rot ) ;
GetLinearVelocitySim ( linV ) ;
GetAngularVelocitySim ( angV ) ;
}
void plPXPhysical : : SetSyncState ( hsPoint3 * pos , hsQuat * rot , hsVector3 * linV , hsVector3 * angV )
{
bool initialSync = plNetClientApp : : GetInstance ( ) - > IsLoadingInitialAgeState ( ) & &
plNetClientApp : : GetInstance ( ) - > GetJoinOrder ( ) = = 0 ;
// If the physical has fallen out of the sim, and this is initial age state, and we're
// the first person in, reset it to the original position. (ie, prop the default state
// we've got right now)
if ( pos & & pos - > fZ < kMaxNegativeZPos & & initialSync )
{
SimLog ( " Physical %s loaded out of range state. Forcing initial state to server. " , GetKeyName ( ) ) ;
DirtySynchState ( kSDLPhysical , plSynchedObject : : kBCastToClients ) ;
return ;
}
if ( pos )
ISetPositionSim ( * pos ) ;
if ( rot )
ISetRotationSim ( * rot ) ;
if ( linV )
SetLinearVelocitySim ( * linV ) ;
if ( angV )
SetAngularVelocitySim ( * angV ) ;
SendNewLocation ( false , true ) ;
}
void plPXPhysical : : ExcludeRegionHack ( hsBool cleared )
{
NxShape * shape = fActor - > getShapes ( ) [ 0 ] ;
shape - > setFlag ( NX_TRIGGER_ON_ENTER , ! cleared ) ;
shape - > setFlag ( NX_TRIGGER_ON_LEAVE , ! cleared ) ;
fGroup = cleared ? plSimDefs : : kGroupExcludeRegion : plSimDefs : : kGroupDetector ;
shape - > setGroup ( fGroup ) ;
/*if switching a static need to inform the controller that it needs to rebuild
the collision cache otherwise will still think that the detector is still static or that
the static is still a detector */
plPXPhysicalControllerCore : : RebuildCache ( ) ;
}
hsBool plPXPhysical : : OverlapWithCapsule ( NxCapsule & cap )
{
NxShape * shape = fActor - > getShapes ( ) [ 0 ] ;
return shape - > checkOverlapCapsule ( cap ) ;
}
hsBool plPXPhysical : : IsDynamic ( ) const
{
return fGroup = = plSimDefs : : kGroupDynamic & &
! GetProperty ( plSimulationInterface : : kPhysAnim ) ;
}
# include "../plSurface/hsGMaterial.h"
# include "../plSurface/plLayerInterface.h"
// Some helper functions for pulling info out of a PhysX trimesh description
inline hsPoint3 & GetTrimeshVert ( NxTriangleMeshDesc & desc , int idx )
{
return * ( ( hsPoint3 * ) ( ( ( char * ) desc . points ) + desc . pointStrideBytes * idx ) ) ;
}
void GetTrimeshTri ( NxTriangleMeshDesc & desc , int idx , UInt16 * out )
{
if ( hsCheckBits ( desc . flags , NX_MF_16_BIT_INDICES ) )
{
UInt16 * descTris = ( ( UInt16 * ) ( ( ( char * ) desc . triangles ) + desc . pointStrideBytes * idx ) ) ;
out [ 0 ] = descTris [ 0 ] ;
out [ 1 ] = descTris [ 1 ] ;
out [ 2 ] = descTris [ 2 ] ;
}
else
{
UInt32 * descTris = ( ( UInt32 * ) ( ( ( char * ) desc . triangles ) + desc . pointStrideBytes * idx ) ) ;
out [ 0 ] = ( UInt16 ) descTris [ 0 ] ;
out [ 1 ] = ( UInt16 ) descTris [ 1 ] ;
out [ 2 ] = ( UInt16 ) descTris [ 2 ] ;
}
}
// Some helper functions for pulling info out of a PhysX trimesh description
inline hsPoint3 & GetConvexVert ( NxConvexMeshDesc & desc , int idx )
{
return * ( ( hsPoint3 * ) ( ( ( char * ) desc . points ) + desc . pointStrideBytes * idx ) ) ;
}
void GetConvexTri ( NxConvexMeshDesc & desc , int idx , UInt16 * out )
{
if ( hsCheckBits ( desc . flags , NX_MF_16_BIT_INDICES ) )
{
UInt16 * descTris = ( ( UInt16 * ) ( ( ( char * ) desc . triangles ) + desc . pointStrideBytes * idx ) ) ;
out [ 0 ] = descTris [ 0 ] ;
out [ 1 ] = descTris [ 1 ] ;
out [ 2 ] = descTris [ 2 ] ;
}
else
{
UInt32 * descTris = ( ( UInt32 * ) ( ( ( char * ) desc . triangles ) + desc . pointStrideBytes * idx ) ) ;
out [ 0 ] = ( UInt16 ) descTris [ 0 ] ;
out [ 1 ] = ( UInt16 ) descTris [ 1 ] ;
out [ 2 ] = ( UInt16 ) descTris [ 2 ] ;
}
}
// Make a visible object that can be viewed by users for debugging purposes.
plDrawableSpans * plPXPhysical : : CreateProxy ( hsGMaterial * mat , hsTArray < UInt32 > & idx , plDrawableSpans * addTo )
{
plDrawableSpans * myDraw = addTo ;
hsMatrix44 l2w , unused ;
GetTransform ( l2w , unused ) ;
hsBool blended = ( ( mat - > GetLayer ( 0 ) - > GetBlendFlags ( ) & hsGMatState : : kBlendMask ) ) ;
NxShape * shape = fActor - > getShapes ( ) [ 0 ] ;
NxTriangleMeshShape * trimeshShape = shape - > isTriangleMesh ( ) ;
if ( trimeshShape )
{
NxTriangleMeshDesc desc ;
trimeshShape - > getTriangleMesh ( ) . saveToDesc ( desc ) ;
hsTArray < hsPoint3 > pos ;
hsTArray < UInt16 > tris ;
const int kMaxTris = 10000 ;
const int kMaxVerts = 32000 ;
if ( ( desc . numVertices < kMaxVerts ) & & ( desc . numTriangles < kMaxTris ) )
{
pos . SetCount ( desc . numVertices ) ;
tris . SetCount ( desc . numTriangles * 3 ) ;
for ( int i = 0 ; i < desc . numVertices ; i + + )
pos [ i ] = GetTrimeshVert ( desc , i ) ;
for ( int i = 0 ; i < desc . numTriangles ; i + + )
GetTrimeshTri ( desc , i , & tris [ i * 3 ] ) ;
myDraw = plDrawableGenerator : : GenerateDrawable ( pos . GetCount ( ) ,
pos . AcquireArray ( ) ,
nil , // normals - def to avg (smooth) norm
nil , // uvws
0 , // uvws per vertex
nil , // colors - def to white
true , // do a quick fake shade
nil , // optional color modulation
tris . GetCount ( ) ,
tris . AcquireArray ( ) ,
mat ,
l2w ,
blended ,
& idx ,
myDraw ) ;
}
else
{
int curTri = 0 ;
int trisToDo = desc . numTriangles ;
while ( trisToDo > 0 )
{
int trisThisRound = trisToDo > kMaxTris ? kMaxTris : trisToDo ;
trisToDo - = trisThisRound ;
pos . SetCount ( trisThisRound * 3 ) ;
tris . SetCount ( trisThisRound * 3 ) ;
for ( int i = 0 ; i < trisThisRound ; i + + )
{
GetTrimeshTri ( desc , curTri , & tris [ i * 3 ] ) ;
pos [ i * 3 + 0 ] = GetTrimeshVert ( desc , tris [ i * 3 + 0 ] ) ;
pos [ i * 3 + 1 ] = GetTrimeshVert ( desc , tris [ i * 3 + 1 ] ) ;
pos [ i * 3 + 2 ] = GetTrimeshVert ( desc , tris [ i * 3 + 2 ] ) ;
curTri + + ;
}
myDraw = plDrawableGenerator : : GenerateDrawable ( pos . GetCount ( ) ,
pos . AcquireArray ( ) ,
nil , // normals - def to avg (smooth) norm
nil , // uvws
0 , // uvws per vertex
nil , // colors - def to white
true , // do a quick fake shade
nil , // optional color modulation
tris . GetCount ( ) ,
tris . AcquireArray ( ) ,
mat ,
l2w ,
blended ,
& idx ,
myDraw ) ;
}
}
}
NxConvexShape * convexShape = shape - > isConvexMesh ( ) ;
if ( convexShape )
{
NxConvexMeshDesc desc ;
convexShape - > getConvexMesh ( ) . saveToDesc ( desc ) ;
hsTArray < hsPoint3 > pos ;
hsTArray < UInt16 > tris ;
pos . SetCount ( desc . numVertices ) ;
tris . SetCount ( desc . numTriangles * 3 ) ;
for ( int i = 0 ; i < desc . numVertices ; i + + )
pos [ i ] = GetConvexVert ( desc , i ) ;
for ( int i = 0 ; i < desc . numTriangles ; i + + )
GetConvexTri ( desc , i , & tris [ i * 3 ] ) ;
myDraw = plDrawableGenerator : : GenerateDrawable ( pos . GetCount ( ) ,
pos . AcquireArray ( ) ,
nil , // normals - def to avg (smooth) norm
nil , // uvws
0 , // uvws per vertex
nil , // colors - def to white
true , // do a quick fake shade
nil , // optional color modulation
tris . GetCount ( ) ,
tris . AcquireArray ( ) ,
mat ,
l2w ,
blended ,
& idx ,
myDraw ) ;
}
NxSphereShape * sphere = shape - > isSphere ( ) ;
if ( sphere )
{
float radius = sphere - > getRadius ( ) ;
hsPoint3 offset = plPXConvert : : Point ( sphere - > getLocalPosition ( ) ) ;
myDraw = plDrawableGenerator : : GenerateSphericalDrawable ( offset , radius ,
mat , l2w , blended ,
nil , & idx , myDraw ) ;
}
NxBoxShape * box = shape - > isBox ( ) ;
if ( box )
{
hsPoint3 dim = plPXConvert : : Point ( box - > getDimensions ( ) ) ;
myDraw = plDrawableGenerator : : GenerateBoxDrawable ( dim . fX * 2.f , dim . fY * 2.f , dim . fZ * 2.f ,
mat , l2w , blended ,
nil , & idx , myDraw ) ;
}
return myDraw ;
}