diff --git a/Engine/source/T3D/physics/physx3/px3.h b/Engine/source/T3D/physics/physx3/px3.h new file mode 100644 index 000000000..e7c8b6555 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3.h @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_H_ +#define _PHYSX3_H_ + +//------------------------------------------------------------------------- +//defines to keep PhysX happy and compiling +#if defined(TORQUE_OS_MAC) && !defined(__APPLE__) + #define __APPLE__ +#elif defined(TORQUE_OS_LINUX) && !defined(LINUX) + #define LINUX +#elif defined(TORQUE_OS_WIN32) && !defined(WIN32) + #define WIN32 +#endif + +//------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern physx::PxPhysics* gPhysics3SDK; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Body.cpp b/Engine/source/T3D/physics/physx3/px3Body.cpp new file mode 100644 index 000000000..e4f437225 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Body.cpp @@ -0,0 +1,421 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Body.h" + +#include "T3D/physics/physx3/px3.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Collision.h" + +#include "console/console.h" +#include "console/consoleTypes.h" + + +Px3Body::Px3Body() : + mActor( NULL ), + mMaterial( NULL ), + mWorld( NULL ), + mBodyFlags( 0 ), + mIsEnabled( true ), + mIsStatic(false) +{ +} + +Px3Body::~Px3Body() +{ + _releaseActor(); +} + +void Px3Body::_releaseActor() +{ + if ( !mActor ) + return; + + mWorld->releaseWriteLock(); + + mActor->userData = NULL; + + mActor->release(); + mActor = NULL; + mBodyFlags = 0; + + if ( mMaterial ) + { + mMaterial->release(); + } + + mColShape = NULL; +} + +bool Px3Body::init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "Px3Body::init - Got a null scene object!" ); + AssertFatal( world, "Px3Body::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "Px3Body::init - The world is the wrong type!" ); + AssertFatal( shape, "Px3Body::init - Got a null collision shape!" ); + AssertFatal( dynamic_cast( shape ), "Px3Body::init - The collision shape is the wrong type!" ); + AssertFatal( !((Px3Collision*)shape)->getShapes().empty(), "Px3Body::init - Got empty collision shape!" ); + + // Cleanup any previous actor. + _releaseActor(); + + mWorld = (Px3World*)world; + mColShape = (Px3Collision*)shape; + mBodyFlags = bodyFlags; + + const bool isKinematic = mBodyFlags & BF_KINEMATIC; + const bool isTrigger = mBodyFlags & BF_TRIGGER; + const bool isDebris = mBodyFlags & BF_DEBRIS; + + if ( isKinematic ) + { + mActor = gPhysics3SDK->createRigidDynamic(physx::PxTransform(physx::PxIDENTITY())); + physx::PxRigidDynamic *actor = mActor->is(); + actor->setRigidDynamicFlag(physx::PxRigidDynamicFlag::eKINEMATIC, true); + actor->setMass(getMax( mass, 1.0f )); + } + else if ( mass > 0.0f ) + { + mActor = gPhysics3SDK->createRigidDynamic(physx::PxTransform(physx::PxIDENTITY())); + } + else + { + mActor = gPhysics3SDK->createRigidStatic(physx::PxTransform(physx::PxIDENTITY())); + mIsStatic = true; + } + + mMaterial = gPhysics3SDK->createMaterial(0.6f,0.4f,0.1f); + + // Add all the shapes. + const Vector &shapes = mColShape->getShapes(); + for ( U32 i=0; i < shapes.size(); i++ ) + { + Px3CollisionDesc* desc = shapes[i]; + if( mass > 0.0f ) + { + if(desc->pGeometry->getType() == physx::PxGeometryType::eTRIANGLEMESH) + { + Con::errorf("PhysX3 Dynamic Triangle Mesh is not supported."); + } + } + physx::PxShape * pShape = mActor->createShape(*desc->pGeometry,*mMaterial,desc->pose); + physx::PxFilterData colData; + if(isDebris) + colData.word0 = PX3_DEBRIS; + else if(isTrigger) + { + //We don't want trigger shapes taking part in shape pair intersection tests + pShape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, false); + colData.word0 = PX3_TRIGGER; + } + else + colData.word0 = PX3_DEFAULT; + + //set the skin width + pShape->setContactOffset(0.01f); + pShape->setFlag(physx::PxShapeFlag::eSCENE_QUERY_SHAPE,true); + pShape->setSimulationFilterData(colData); + pShape->setQueryFilterData(colData); + } + + //mass & intertia has to be set after creating the shape + if ( mass > 0.0f ) + { + physx::PxRigidDynamic *actor = mActor->is(); + physx::PxRigidBodyExt::setMassAndUpdateInertia(*actor,mass); + } + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + mWorld->getScene()->addActor(*mActor); + mIsEnabled = true; + + if ( isDebris ) + mActor->setDominanceGroup( 31 ); + + mUserData.setObject( obj ); + mUserData.setBody( this ); + mActor->userData = &mUserData; + + return true; +} + +void Px3Body::setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ) +{ + AssertFatal( mActor, "Px3Body::setMaterial - The actor is null!" ); + + if ( isDynamic() ) + { + physx::PxRigidDynamic *actor = mActor->is(); + actor->wakeUp(); + } + + mMaterial->setRestitution(restitution); + mMaterial->setStaticFriction(staticFriction); + mMaterial->setDynamicFriction(friction); + +} + +void Px3Body::setSleepThreshold( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "Px3Body::setSleepThreshold - The actor is null!" ); + + if(mIsStatic) + return; + + physx::PxRigidDynamic *actor = mActor->is(); + physx::PxF32 massNormalized= (linear*linear+angular*angular)/2.0f; + actor->setSleepThreshold(massNormalized); +} + +void Px3Body::setDamping( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "Px3Body::setDamping - The actor is null!" ); + if(mIsStatic) + return; + + physx::PxRigidDynamic *actor = mActor->is(); + actor->setLinearDamping( linear ); + actor->setAngularDamping( angular ); +} + +void Px3Body::getState( PhysicsState *outState ) +{ + AssertFatal( mActor, "Px3Body::getState - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::getState - This call is only for dynamics!" ); + + outState->position = px3Cast( mActor->getGlobalPose().p ); + outState->orientation = px3Cast( mActor->getGlobalPose().q ); + + physx::PxRigidDynamic *actor = mActor->is(); + outState->linVelocity = px3Cast( actor->getLinearVelocity() ); + outState->angVelocity = px3Cast( actor->getAngularVelocity() ); + outState->sleeping = actor->isSleeping(); + outState->momentum = px3Cast( (1.0f/actor->getMass()) * actor->getLinearVelocity() );//?? + +} + +F32 Px3Body::getMass() const +{ + AssertFatal( mActor, "PxBody::getCMassPosition - The actor is null!" ); + if(mIsStatic) + return 0; + + const physx::PxRigidDynamic *actor = mActor->is(); + return actor->getMass(); +} + +Point3F Px3Body::getCMassPosition() const +{ + AssertFatal( mActor, "Px3Body::getCMassPosition - The actor is null!" ); + if(mIsStatic) + return px3Cast(mActor->getGlobalPose().p); + + physx::PxRigidDynamic *actor = mActor->is(); + physx::PxTransform pose = actor->getGlobalPose() * actor->getCMassLocalPose(); + return px3Cast(pose.p); +} + +void Px3Body::setLinVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "Px3Body::setLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::setLinVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + actor->setLinearVelocity( px3Cast( vel ) ); +} + +void Px3Body::setAngVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "Px3Body::setAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::setAngVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + actor->setAngularVelocity(px3Cast( vel ) ); +} + +Point3F Px3Body::getLinVelocity() const +{ + AssertFatal( mActor, "Px3Body::getLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::getLinVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + return px3Cast( actor->getLinearVelocity() ); +} + +Point3F Px3Body::getAngVelocity() const +{ + AssertFatal( mActor, "Px3Body::getAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::getAngVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + return px3Cast( actor->getAngularVelocity() ); +} + +void Px3Body::setSleeping( bool sleeping ) +{ + AssertFatal( mActor, "Px3Body::setSleeping - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::setSleeping - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + if ( sleeping ) + actor->putToSleep(); + else + actor->wakeUp(); +} + +bool Px3Body::isDynamic() const +{ + AssertFatal( mActor, "PxBody::isDynamic - The actor is null!" ); + return !mIsStatic && ( mBodyFlags & BF_KINEMATIC ) == 0; +} + +PhysicsWorld* Px3Body::getWorld() +{ + return mWorld; +} + +PhysicsCollision* Px3Body::getColShape() +{ + return mColShape; +} + +MatrixF& Px3Body::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mActor, "Px3Body::getTransform - The actor is null!" ); + + *outMatrix = px3Cast(mActor->getGlobalPose()); + return *outMatrix; +} + +Box3F Px3Body::getWorldBounds() +{ + AssertFatal( mActor, "Px3Body::getTransform - The actor is null!" ); + + physx::PxBounds3 bounds; + bounds.setEmpty(); + physx::PxBounds3 shapeBounds; + + + U32 shapeCount = mActor->getNbShapes(); + physx::PxShape **shapes = new physx::PxShape*[shapeCount]; + mActor->getShapes(shapes, shapeCount); + for ( U32 i = 0; i < shapeCount; i++ ) + { + // Get the shape's bounds. + shapeBounds = physx::PxShapeExt::getWorldBounds(*shapes[i],*mActor); + // Combine them into the total bounds. + bounds.include( shapeBounds ); + } + + delete [] shapes; + + return px3Cast( bounds ); +} + +void Px3Body::setSimulationEnabled( bool enabled ) +{ + if ( mIsEnabled == enabled ) + return; + + //Don't need to enable/disable eSIMULATION_SHAPE for trigger,it's disabled permanently + if(mBodyFlags & BF_TRIGGER) + return; + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + U32 shapeCount = mActor->getNbShapes(); + physx::PxShape **shapes = new physx::PxShape*[shapeCount]; + mActor->getShapes(shapes, shapeCount); + for ( S32 i = 0; i < mActor->getNbShapes(); i++ ) + { + shapes[i]->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,!mIsEnabled);//????? + } + + delete [] shapes; +} + +void Px3Body::setTransform( const MatrixF &transform ) +{ + AssertFatal( mActor, "Px3Body::setTransform - The actor is null!" ); + + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + + mActor->setGlobalPose(px3Cast(transform),false); + + if(mIsStatic) + return; + + physx::PxRigidDynamic *actor = mActor->is(); + bool kinematic = actor->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC; + // If its dynamic we have more to do. + if ( isDynamic() && !kinematic ) + { + actor->setLinearVelocity( physx::PxVec3(0) ); + actor->setAngularVelocity( physx::PxVec3(0) ); + actor->wakeUp(); + } +} + +void Px3Body::applyCorrection( const MatrixF &transform ) +{ + AssertFatal( mActor, "Px3Body::applyCorrection - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::applyCorrection - This call is only for dynamics!" ); + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + mActor->setGlobalPose( px3Cast(transform) ); +} + +void Px3Body::applyImpulse( const Point3F &origin, const Point3F &force ) +{ + AssertFatal( mActor, "Px3Body::applyImpulse - The actor is null!" ); + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + physx::PxRigidDynamic *actor = mActor->is(); + if ( mIsEnabled && isDynamic() ) + physx::PxRigidBodyExt::addForceAtPos(*actor,px3Cast(force), + px3Cast(origin), + physx::PxForceMode::eIMPULSE); + +} + diff --git a/Engine/source/T3D/physics/physx3/px3Body.h b/Engine/source/T3D/physics/physx3/px3Body.h new file mode 100644 index 000000000..379d5b4cd --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Body.h @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3BODY_H_ +#define _T3D_PHYSICS_PX3BODY_H_ + +#ifndef _T3D_PHYSICS_PHYSICSBODY_H_ +#include "T3D/physics/physicsBody.h" +#endif +#ifndef _PHYSICS_PHYSICSUSERDATA_H_ +#include "T3D/physics/physicsUserData.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +class Px3World; +class Px3Collision; +struct Px3CollisionDesc; + +namespace physx{ + class PxRigidActor; + class PxMaterial; + class PxShape; +} + + +class Px3Body : public PhysicsBody +{ +protected: + + /// The physics world we are in. + Px3World *mWorld; + + /// The physics actor. + physx::PxRigidActor *mActor; + + /// The unshared local material used on all the + /// shapes on this actor. + physx::PxMaterial *mMaterial; + + /// We hold the collision reference as it contains + /// allocated objects that we own and must free. + StrongRefPtr mColShape; + + /// + MatrixF mInternalTransform; + + /// The body flags set at creation time. + U32 mBodyFlags; + + /// Is true if this body is enabled and active + /// in the simulation of the scene. + bool mIsEnabled; + bool mIsStatic; + + /// + void _releaseActor(); + + +public: + + Px3Body(); + virtual ~Px3Body(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &xfm ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual Box3F getWorldBounds(); + virtual void setSimulationEnabled( bool enabled ); + virtual bool isSimulationEnabled() { return mIsEnabled; } + + // PhysicsBody + virtual bool init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ); + virtual bool isDynamic() const; + virtual PhysicsCollision* getColShape(); + virtual void setSleepThreshold( F32 linear, F32 angular ); + virtual void setDamping( F32 linear, F32 angular ); + virtual void getState( PhysicsState *outState ); + virtual F32 getMass() const; + virtual Point3F getCMassPosition() const; + virtual void setLinVelocity( const Point3F &vel ); + virtual void setAngVelocity( const Point3F &vel ); + virtual Point3F getLinVelocity() const; + virtual Point3F getAngVelocity() const; + virtual void setSleeping( bool sleeping ); + virtual void setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ); + virtual void applyCorrection( const MatrixF &xfm ); + virtual void applyImpulse( const Point3F &origin, const Point3F &force ); +}; + +#endif diff --git a/Engine/source/T3D/physics/physx3/px3Cast.h b/Engine/source/T3D/physics/physx3/px3Cast.h new file mode 100644 index 000000000..971e830c8 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Cast.h @@ -0,0 +1,137 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_CASTS_H_ +#define _PHYSX3_CASTS_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif +#ifndef _MTRANSFORM_H_ +#include "math/mTransform.h" +#endif + + +template inline T px3Cast( const F &from ); + +//------------------------------------------------------------------------- + +template<> +inline Point3F px3Cast( const physx::PxVec3 &vec ) +{ + return Point3F( vec.x, vec.y, vec.z ); +} + +template<> +inline physx::PxVec3 px3Cast( const Point3F &point ) +{ + return physx::PxVec3( point.x, point.y, point.z ); +} +//------------------------------------------------------------------------- +template<> +inline QuatF px3Cast( const physx::PxQuat &quat ) +{ + /// The Torque quat has the opposite winding order. + return QuatF( -quat.x, -quat.y, -quat.z, quat.w ); +} + +template<> +inline physx::PxQuat px3Cast( const QuatF &quat ) +{ + /// The Torque quat has the opposite winding order. + physx::PxQuat result( -quat.x, -quat.y, -quat.z, quat.w ); + return result; +} +//------------------------------------------------------------------------- + +template<> +inline physx::PxExtendedVec3 px3Cast( const Point3F &point ) +{ + return physx::PxExtendedVec3( point.x, point.y, point.z ); +} + +template<> +inline Point3F px3Cast( const physx::PxExtendedVec3 &xvec ) +{ + return Point3F( xvec.x, xvec.y, xvec.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline physx::PxBounds3 px3Cast( const Box3F &box ) +{ + physx::PxBounds3 bounds(px3Cast(box.minExtents), + px3Cast(box.maxExtents)); + return bounds; +} + +template<> +inline Box3F px3Cast( const physx::PxBounds3 &bounds ) +{ + return Box3F( bounds.minimum.x, + bounds.minimum.y, + bounds.minimum.z, + bounds.maximum.x, + bounds.maximum.y, + bounds.maximum.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline physx::PxTransform px3Cast( const MatrixF &xfm ) +{ + physx::PxTransform out; + QuatF q; + q.set(xfm); + out.q = px3Cast(q); + out.p = px3Cast(xfm.getPosition()); + return out; +} + +template<> +inline TransformF px3Cast(const physx::PxTransform &xfm) +{ + TransformF out(px3Cast(xfm.p),AngAxisF(px3Cast(xfm.q))); + return out; +} + +template<> +inline MatrixF px3Cast( const physx::PxTransform &xfm ) +{ + MatrixF out; + TransformF t = px3Cast(xfm); + out = t.getMatrix(); + return out; +} + +#endif diff --git a/Engine/source/T3D/physics/physx3/px3Collision.cpp b/Engine/source/T3D/physics/physx3/px3Collision.cpp new file mode 100644 index 000000000..fb11a4400 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Collision.cpp @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Collision.h" + +#include "math/mPoint3.h" +#include "math/mMatrix.h" +#include "T3D/physics/physx3/px3.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Stream.h" + + +Px3Collision::Px3Collision() +{ +} + +Px3Collision::~Px3Collision() +{ + + for ( U32 i=0; i < mColShapes.size(); i++ ) + { + Px3CollisionDesc *desc = mColShapes[i]; + delete desc->pGeometry; + // Delete the descriptor. + delete desc; + } + + mColShapes.clear(); +} + +void Px3Collision::addPlane( const PlaneF &plane ) +{ + physx::PxVec3 pos = px3Cast(plane.getPosition()); + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxPlaneGeometry(); + desc->pose = physx::PxTransform(pos, physx::PxQuat(physx::PxHalfPi, physx::PxVec3(0.0f, -1.0f, 0.0f))); + mColShapes.push_back(desc); +} + +void Px3Collision::addBox( const Point3F &halfWidth,const MatrixF &localXfm ) +{ + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxBoxGeometry(px3Cast(halfWidth)); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); +} + +void Px3Collision::addSphere( F32 radius, + const MatrixF &localXfm ) +{ + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxSphereGeometry(radius); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); +} + +void Px3Collision::addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ) +{ + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxCapsuleGeometry(radius,height*0.5);//uses half height + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); +} + +bool Px3Collision::addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ) +{ + physx::PxCooking *cooking = Px3World::getCooking(); + physx::PxConvexMeshDesc convexDesc; + convexDesc.points.data = points; + convexDesc.points.stride = sizeof(Point3F); + convexDesc.points.count = count; + convexDesc.flags = physx::PxConvexFlag::eFLIPNORMALS|physx::PxConvexFlag::eCOMPUTE_CONVEX | physx::PxConvexFlag::eINFLATE_CONVEX; + + Px3MemOutStream stream; + if(!cooking->cookConvexMesh(convexDesc,stream)) + return false; + + physx::PxConvexMesh* convexMesh; + Px3MemInStream in(stream.getData(), stream.getSize()); + convexMesh = gPhysics3SDK->createConvexMesh(in); + + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxConvexMeshGeometry(convexMesh); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); + return true; +} + +bool Px3Collision::addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ) +{ + physx::PxCooking *cooking = Px3World::getCooking(); + physx::PxTriangleMeshDesc meshDesc; + meshDesc.points.count = vertCount; + meshDesc.points.data = vert; + meshDesc.points.stride = sizeof(Point3F); + + meshDesc.triangles.count = triCount; + meshDesc.triangles.data = index; + meshDesc.triangles.stride = 3*sizeof(U32); + meshDesc.flags = physx::PxMeshFlag::eFLIPNORMALS; + + Px3MemOutStream stream; + if(!cooking->cookTriangleMesh(meshDesc,stream)) + return false; + + physx::PxTriangleMesh *mesh; + Px3MemInStream in(stream.getData(), stream.getSize()); + mesh = gPhysics3SDK->createTriangleMesh(in); + + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxTriangleMeshGeometry(mesh); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); + return true; +} + +bool Px3Collision::addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ) +{ + const F32 heightScale = 0.03125f; + physx::PxHeightFieldSample* samples = (physx::PxHeightFieldSample*) new physx::PxHeightFieldSample[blockSize*blockSize]; + memset(samples,0,blockSize*blockSize*sizeof(physx::PxHeightFieldSample)); + + physx::PxHeightFieldDesc heightFieldDesc; + heightFieldDesc.nbColumns = blockSize; + heightFieldDesc.nbRows = blockSize; + heightFieldDesc.thickness = -10.f; + heightFieldDesc.convexEdgeThreshold = 0; + heightFieldDesc.format = physx::PxHeightFieldFormat::eS16_TM; + heightFieldDesc.samples.data = samples; + heightFieldDesc.samples.stride = sizeof(physx::PxHeightFieldSample); + + physx::PxU8 *currentByte = (physx::PxU8*)heightFieldDesc.samples.data; + for ( U32 row = 0; row < blockSize; row++ ) + { + const U32 tess = ( row + 1 ) % 2; + + for ( U32 column = 0; column < blockSize; column++ ) + { + physx::PxHeightFieldSample *currentSample = (physx::PxHeightFieldSample*)currentByte; + + U32 index = ( blockSize - row - 1 ) + ( column * blockSize ); + currentSample->height = (physx::PxI16)heights[ index ]; + + + if ( holes && holes[ getMax( (S32)index - 1, 0 ) ] ) // row index for holes adjusted so PhysX collision shape better matches rendered terrain + { + currentSample->materialIndex0 = 0; + currentSample->materialIndex1 = 0; + } + else + { + currentSample->materialIndex0 = 1; + currentSample->materialIndex1 = 1; + } + + int flag = ( column + tess ) % 2; + if(flag) + currentSample->setTessFlag(); + else + currentSample->clearTessFlag(); + + currentByte += heightFieldDesc.samples.stride; + } + } + + physx::PxHeightField * hf = gPhysics3SDK->createHeightField(heightFieldDesc); + physx::PxHeightFieldGeometry *geom = new physx::PxHeightFieldGeometry(hf,physx::PxMeshGeometryFlags(),heightScale,metersPerSample,metersPerSample); + + physx::PxTransform pose= physx::PxTransform(physx::PxQuat(Float_HalfPi, physx::PxVec3(1, 0, 0 ))); + physx::PxTransform pose1= physx::PxTransform(physx::PxQuat(Float_Pi, physx::PxVec3(0, 0, 1 ))); + physx::PxTransform pose2 = pose1 * pose; + pose2.p = physx::PxVec3(( blockSize - 1 ) * metersPerSample, 0, 0 ); + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = geom; + desc->pose = pose2; + + mColShapes.push_back(desc); + return true; +} diff --git a/Engine/source/T3D/physics/physx3/px3Collision.h b/Engine/source/T3D/physics/physx3/px3Collision.h new file mode 100644 index 000000000..857f4f5e3 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Collision.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3COLLISION_H_ +#define _T3D_PHYSICS_PX3COLLISION_H_ + +#ifndef _T3D_PHYSICS_PHYSICSCOLLISION_H_ +#include "T3D/physics/physicsCollision.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +//nasty hate doing this! +#include + +//forward declare +namespace physx{class PxGeometry;} + + +struct Px3CollisionDesc +{ + physx::PxGeometry *pGeometry; + physx::PxTransform pose; +}; + +class Px3Collision : public PhysicsCollision +{ + typedef Vector Px3CollisionList; +protected: + /// The collision representation. + Px3CollisionList mColShapes; + +public: + + Px3Collision(); + virtual ~Px3Collision(); + + /// Return the PhysX shape descriptions. + const Px3CollisionList& getShapes() const { return mColShapes; } + + // PhysicsCollision + virtual void addPlane( const PlaneF &plane ); + virtual void addBox( const Point3F &halfWidth, + const MatrixF &localXfm ); + virtual void addSphere( F32 radius, + const MatrixF &localXfm ); + virtual void addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ); + virtual bool addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ); + virtual bool addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ); + virtual bool addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Player.cpp b/Engine/source/T3D/physics/physx3/px3Player.cpp new file mode 100644 index 000000000..1c44068e9 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Player.cpp @@ -0,0 +1,328 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Player.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3Utils.h" +#include "collision/collision.h" + + +Px3Player::Px3Player() + : PhysicsPlayer(), + mController( NULL ), + mWorld( NULL ), + mObject( NULL ), + mSkinWidth( 0.05f ), + mOriginOffset( 0.0f ), + mElapsed(0) +{ + PHYSICSMGR->getPhysicsResetSignal().notify( this, &Px3Player::_onPhysicsReset ); +} + +Px3Player::~Px3Player() +{ + _releaseController(); + PHYSICSMGR->getPhysicsResetSignal().remove( this, &Px3Player::_onPhysicsReset ); +} + +void Px3Player::_releaseController() +{ + if ( mController ) + { + mController->getActor()->userData = NULL; + mWorld->getStaticChangedSignal().remove( this, &Px3Player::_onStaticChanged ); + mController->release(); + } +} + +void Px3Player::init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "Px3Player::init - Got a null scene object!" ); + AssertFatal( world, "Px3Player::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "Px3Player::init - The world is the wrong type!" ); + + // Cleanup any previous controller. + _releaseController(); + + mObject = obj; + mWorld = (Px3World*)world; + mOriginOffset = size.z * 0.5f; + + physx::PxCapsuleControllerDesc desc; + desc.contactOffset = mSkinWidth; + desc.radius = getMax( size.x, size.y ) * 0.5f; + desc.radius -= mSkinWidth; + desc.height = size.z - ( desc.radius * 2.0f ); + desc.height -= mSkinWidth * 2.0f; + desc.climbingMode = physx::PxCapsuleClimbingMode::eCONSTRAINED; + desc.position.set( 0, 0, 0 ); + desc.upDirection = physx::PxVec3(0,0,1); + desc.reportCallback = this; + desc.slopeLimit = runSurfaceCos; + desc.stepOffset = stepHeight; + desc.behaviorCallback = NULL; + desc.material = gPhysics3SDK->createMaterial(0.1f, 0.1f, 0.2f); + + mController = mWorld->createController( desc ); + + mWorld->getStaticChangedSignal().notify( this, &Px3Player::_onStaticChanged ); + physx::PxRigidDynamic *kineActor = mController->getActor(); + + //player only has one shape + physx::PxShape *shape = px3GetFirstShape(kineActor); + physx::PxFilterData colData; + colData.word0 = PX3_PLAYER; + shape->setSimulationFilterData(colData); + shape->setQueryFilterData(colData); + + //store geometry for later use in findContact calls + shape->getCapsuleGeometry(mGeometry); + + mUserData.setObject( obj ); + kineActor->userData = &mUserData; + +} + +void Px3Player::_onStaticChanged() +{ + if(mController) + mController->invalidateCache(); +} + +void Px3Player::_onPhysicsReset( PhysicsResetEvent reset ) +{ + if(mController) + mController->invalidateCache(); +} + +Point3F Px3Player::move( const VectorF &disp, CollisionList &outCol ) +{ + AssertFatal( mController, "Px3Player::move - The controller is null!" ); + + // Return the last position if the simulation is stopped. + // + // See PxPlayer::_onPhysicsReset + if ( !mWorld->isEnabled() ) + { + Point3F newPos = px3Cast( mController->getPosition() ); + newPos.z -= mOriginOffset; + return newPos; + } + + mWorld->releaseWriteLock(); + + mCollisionList = &outCol; + + physx::PxVec3 dispNx( disp.x, disp.y, disp.z ); + if (mIsZero(disp.z)) + dispNx.z = 0.0f; + + U32 groups = 0xffffffff; + groups &= ~( PX3_TRIGGER ); // No trigger shapes! + groups &= ~( PX3_DEBRIS); + physx::PxControllerFilters filter; + physx::PxFilterData data; + data.word0=groups; + filter.mFilterData = &data; + filter.mFilterFlags = physx::PxSceneQueryFilterFlags(physx::PxControllerFlag::eCOLLISION_DOWN|physx::PxControllerFlag::eCOLLISION_SIDES|physx::PxControllerFlag::eCOLLISION_UP); + + mController->move( dispNx,0.0001f,0, filter ); + + Point3F newPos = px3Cast( mController->getPosition() ); + newPos.z -= mOriginOffset; + + mCollisionList = NULL; + + return newPos; +} + +void Px3Player::onShapeHit( const physx::PxControllerShapeHit& hit ) +{ + if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions) + return; + + physx::PxRigidActor *actor = hit.actor; + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + // Fill out the Collision + // structure for use later. + Collision &col = mCollisionList->increment(); + dMemset( &col, 0, sizeof( col ) ); + + col.normal = px3Cast( hit.worldNormal ); + col.point = px3Cast( hit.worldPos ); + col.distance = hit.length; + if ( userData ) + col.object = userData->getObject(); + + if (mIsZero(hit.dir.z)) + { + if (col.normal.z > 0.0f) + { + col.normal.z = 0.0f; + col.normal.normalizeSafe(); + } + } + else + { + col.normal.set(0.0f, 0.0f, 1.0f); + } + + +} + +void Px3Player::onControllerHit( const physx::PxControllersHit& hit ) +{ + if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions) + return; + + physx::PxRigidActor *actor = hit.other->getActor(); + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + // For controller-to-controller hit we don't have an actual hit point, so all + // we can do is set the hit object. + Collision &col = mCollisionList->increment(); + dMemset( &col, 0, sizeof( col ) ); + if ( userData ) + col.object = userData->getObject(); + +} + +void Px3Player::findContact( SceneObject **contactObject, + VectorF *contactNormal, + Vector *outOverlapObjects ) const +{ + // Calculate the sweep motion... + F32 halfCapSize = mOriginOffset; + F32 halfSmallCapSize = halfCapSize * 0.8f; + F32 diff = halfCapSize - halfSmallCapSize; + + F32 distance = diff + mSkinWidth + 0.01f; + physx::PxVec3 dir(0,0,-1); + + physx::PxScene *scene = mWorld->getScene(); + physx::PxHitFlags hitFlags(physx::PxHitFlag::eDEFAULT); + physx::PxQueryFilterData filterData(physx::PxQueryFlag::eDYNAMIC|physx::PxQueryFlag::eSTATIC); + filterData.data.word0 = PX3_DEFAULT; + physx::PxSweepHit sweepHit; + physx::PxRigidDynamic *actor= mController->getActor(); + physx::PxU32 shapeIndex; + + bool hit = physx::PxRigidBodyExt::linearSweepSingle(*actor,*scene,dir,distance,hitFlags,sweepHit,shapeIndex,filterData); + if ( hit ) + { + PhysicsUserData *data = PhysicsUserData::cast( sweepHit.actor->userData); + if ( data ) + { + *contactObject = data->getObject(); + *contactNormal = px3Cast( sweepHit.normal ); + } + } + + // Check for overlapped objects ( triggers ) + + if ( !outOverlapObjects ) + return; + + filterData.data.word0 = PX3_TRIGGER; + + const physx::PxU32 bufferSize = 10; + physx::PxOverlapBufferN hitBuffer; + hit = scene->overlap(mGeometry,actor->getGlobalPose(),hitBuffer,filterData); + if(hit) + { + for ( U32 i = 0; i < hitBuffer.nbTouches; i++ ) + { + PhysicsUserData *data = PhysicsUserData::cast( hitBuffer.touches[i].actor->userData ); + if ( data ) + outOverlapObjects->push_back( data->getObject() ); + } + } + +} + +void Px3Player::enableCollision() +{ + AssertFatal( mController, "Px3Player::enableCollision - The controller is null!" ); + + mWorld->releaseWriteLock(); + px3GetFirstShape(mController->getActor())->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,true); +} + +void Px3Player::disableCollision() +{ + AssertFatal( mController, "Px3Player::disableCollision - The controller is null!" ); + + mWorld->releaseWriteLock(); + px3GetFirstShape(mController->getActor())->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,false); +} + +PhysicsWorld* Px3Player::getWorld() +{ + return mWorld; +} + +void Px3Player::setTransform( const MatrixF &transform ) +{ + AssertFatal( mController, "Px3Player::setTransform - The controller is null!" ); + + mWorld->releaseWriteLock(); + + Point3F newPos = transform.getPosition(); + newPos.z += mOriginOffset; + + const Point3F &curPos = px3Cast(mController->getPosition()); + + if ( !(newPos - curPos ).isZero() ) + mController->setPosition( px3Cast(newPos) ); +} + +MatrixF& Px3Player::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mController, "Px3Player::getTransform - The controller is null!" ); + + Point3F newPos = px3Cast( mController->getPosition() ); + newPos.z -= mOriginOffset; + outMatrix->setPosition( newPos ); + + return *outMatrix; +} + +void Px3Player::setScale( const Point3F &scale ) +{ + //Ignored +} + +Box3F Px3Player::getWorldBounds() +{ + Con::warnf( "Px3Player::getWorldBounds - not implemented" ); + return Box3F::Invalid; +} + diff --git a/Engine/source/T3D/physics/physx3/px3Player.h b/Engine/source/T3D/physics/physx3/px3Player.h new file mode 100644 index 000000000..c8dee08e6 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Player.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PX3PLAYER_H +#define _PX3PLAYER_H + +#ifndef _PHYSX3_H_ +#include "T3D/physics/physx3/px3.h" +#endif +#ifndef _T3D_PHYSICS_PHYSICSPLAYER_H_ +#include "T3D/physics/physicsPlayer.h" +#endif +#ifndef _T3D_PHYSICSCOMMON_H_ +#include "T3D/physics/physicsCommon.h" +#endif + +class Px3World; + +class Px3Player : public PhysicsPlayer, public physx::PxUserControllerHitReport +{ +protected: + + physx::PxController *mController; + physx::PxCapsuleGeometry mGeometry; + F32 mSkinWidth; + + Px3World *mWorld; + + SceneObject *mObject; + + /// Used to get collision info out of the + /// PxUserControllerHitReport callbacks. + CollisionList *mCollisionList; + + /// + F32 mOriginOffset; + + /// + F32 mStepHeight; + U32 mElapsed; + /// + void _releaseController(); + + + virtual void onShapeHit( const physx::PxControllerShapeHit &hit ); + virtual void onControllerHit( const physx::PxControllersHit &hit ); + virtual void onObstacleHit(const physx::PxControllerObstacleHit &){} + + void _findContact( SceneObject **contactObject, VectorF *contactNormal ) const; + + void _onPhysicsReset( PhysicsResetEvent reset ); + + void _onStaticChanged(); + +public: + + Px3Player(); + virtual ~Px3Player(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &transform ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual void setScale( const Point3F &scale ); + virtual Box3F getWorldBounds(); + virtual void setSimulationEnabled( bool enabled ) {} + virtual bool isSimulationEnabled() { return true; } + + // PhysicsPlayer + virtual void init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ); + virtual Point3F move( const VectorF &displacement, CollisionList &outCol ); + virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector *outOverlapObjects ) const; + virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const { return true; } + virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) {} + virtual void enableCollision(); + virtual void disableCollision(); +}; + + +#endif // _PXPLAYER_H \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Plugin.cpp b/Engine/source/T3D/physics/physx3/px3Plugin.cpp new file mode 100644 index 000000000..9da0ca026 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Plugin.cpp @@ -0,0 +1,227 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/consoleTypes.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Plugin.h" +#include "T3D/physics/physx3/px3Collision.h" +#include "T3D/physics/physx3/px3Body.h" +#include "T3D/physics/physx3/px3Player.h" + +#include "T3D/physics/physicsShape.h" +#include "T3D/gameBase/gameProcess.h" +#include "core/util/tNamedFactory.h" + + +AFTER_MODULE_INIT( Sim ) +{ + NamedFactory::add( "PhysX3", &Px3Plugin::create ); + + #if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + NamedFactory::add( "default", &Px3Plugin::create ); + #endif +} + +PhysicsPlugin* Px3Plugin::create() +{ + // Only create the plugin if it hasn't been set up AND + // the PhysX world is successfully initialized. + bool success = Px3World::restartSDK( false ); + if ( success ) + return new Px3Plugin(); + + return NULL; +} + +Px3Plugin::Px3Plugin() +{ +} + +Px3Plugin::~Px3Plugin() +{ +} + +void Px3Plugin::destroyPlugin() +{ + // Cleanup any worlds that are still kicking. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + { + iter->value->destroyWorld(); + delete iter->value; + } + mPhysicsWorldLookup.clear(); + + Px3World::restartSDK( true ); + + delete this; +} + +void Px3Plugin::reset() +{ + // First delete all the cleanup objects. + if ( getPhysicsCleanup() ) + getPhysicsCleanup()->deleteAllObjects(); + + getPhysicsResetSignal().trigger( PhysicsResetEvent_Restore ); + + // Now let each world reset itself. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + iter->value->reset(); +} + +PhysicsCollision* Px3Plugin::createCollision() +{ + return new Px3Collision(); +} + +PhysicsBody* Px3Plugin::createBody() +{ + return new Px3Body(); +} + +PhysicsPlayer* Px3Plugin::createPlayer() +{ + return new Px3Player(); +} + +bool Px3Plugin::isSimulationEnabled() const +{ + bool ret = false; + Px3World *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + { + ret = world->isEnabled(); + return ret; + } + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + { + ret = world->isEnabled(); + return ret; + } + + return ret; +} + +void Px3Plugin::enableSimulation( const String &worldName, bool enable ) +{ + Px3World *world = static_cast( getWorld( worldName ) ); + if ( world ) + world->setEnabled( enable ); +} + +void Px3Plugin::setTimeScale( const F32 timeScale ) +{ + // Grab both the client and + // server worlds and set their time + // scales to the passed value. + Px3World *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); +} + +const F32 Px3Plugin::getTimeScale() const +{ + // Grab both the client and + // server worlds and call + // setEnabled( true ) on them. + Px3World *world = static_cast( getWorld( smClientWorldName ) ); + if ( !world ) + { + world = static_cast( getWorld( smServerWorldName ) ); + if ( !world ) + return 0.0f; + } + + return world->getEditorTimeScale(); +} + +bool Px3Plugin::createWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + PhysicsWorld *world = NULL; + + iter != mPhysicsWorldLookup.end() ? world = (*iter).value : world = NULL; + + if ( world ) + { + Con::errorf( "Px3Plugin::createWorld - %s world already exists!", worldName.c_str() ); + return false; + } + + world = new Px3World(); + + if ( worldName.equal( smClientWorldName, String::NoCase ) ) + world->initWorld( false, ClientProcessList::get() ); + else + world->initWorld( true, ServerProcessList::get() ); + + mPhysicsWorldLookup.insert( worldName, world ); + + return world != NULL; +} + +void Px3Plugin::destroyWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + if ( iter == mPhysicsWorldLookup.end() ) + return; + + PhysicsWorld *world = (*iter).value; + world->destroyWorld(); + delete world; + + mPhysicsWorldLookup.erase( iter ); +} + +PhysicsWorld* Px3Plugin::getWorld( const String &worldName ) const +{ + if ( mPhysicsWorldLookup.isEmpty() ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.find( worldName ); + + return iter != mPhysicsWorldLookup.end() ? (*iter).value : NULL; +} + +PhysicsWorld* Px3Plugin::getWorld() const +{ + if ( mPhysicsWorldLookup.size() == 0 ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.begin(); + return iter->value; +} + +U32 Px3Plugin::getWorldCount() const +{ + return mPhysicsWorldLookup.size(); +} + diff --git a/Engine/source/T3D/physics/physx3/px3Plugin.h b/Engine/source/T3D/physics/physx3/px3Plugin.h new file mode 100644 index 000000000..7972fe59a --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Plugin.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3PLUGIN_H_ +#define _T3D_PHYSICS_PX3PLUGIN_H_ + +#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_ +#include "T3D/physics/physicsPlugin.h" +#endif + +class Px3Plugin : public PhysicsPlugin +{ +public: + + Px3Plugin(); + ~Px3Plugin(); + + /// Create function for factory. + static PhysicsPlugin* create(); + + // PhysicsPlugin + virtual void destroyPlugin(); + virtual void reset(); + virtual PhysicsCollision* createCollision(); + virtual PhysicsBody* createBody(); + virtual PhysicsPlayer* createPlayer(); + virtual bool isSimulationEnabled() const; + virtual void enableSimulation( const String &worldName, bool enable ); + virtual void setTimeScale( const F32 timeScale ); + virtual const F32 getTimeScale() const; + virtual bool createWorld( const String &worldName ); + virtual void destroyWorld( const String &worldName ); + virtual PhysicsWorld* getWorld( const String &worldName ) const; + virtual PhysicsWorld* getWorld() const; + virtual U32 getWorldCount() const; + +}; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Stream.cpp b/Engine/source/T3D/physics/physx3/px3Stream.cpp new file mode 100644 index 000000000..f8374cf89 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Stream.cpp @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Stream.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/strings/stringFunctions.h" + + +Px3MemOutStream::Px3MemOutStream() : mMemStream(1024) +{ +} + +Px3MemOutStream::~Px3MemOutStream() +{ +} + +physx::PxU32 Px3MemOutStream::write(const void *src, physx::PxU32 count) +{ + physx::PxU32 out=0; + if(!mMemStream.write(count,src)) + return out; + + out = count; + return out; +} + +Px3MemInStream::Px3MemInStream(physx::PxU8* data, physx::PxU32 length):mMemStream(length,data) +{ +} + +physx::PxU32 Px3MemInStream::read(void* dest, physx::PxU32 count) +{ + physx::PxU32 read =0; + if(!mMemStream.read(count,dest)) + return read; + + read = count; + return read; +} + +void Px3MemInStream::seek(physx::PxU32 pos) +{ + mMemStream.setPosition(pos); +} + +physx::PxU32 Px3MemInStream::getLength() const +{ + return mMemStream.getStreamSize(); +} + +physx::PxU32 Px3MemInStream::tell() const +{ + return mMemStream.getPosition(); +} + +Px3ConsoleStream::Px3ConsoleStream() +{ +} + +Px3ConsoleStream::~Px3ConsoleStream() +{ +} + +void Px3ConsoleStream::reportError( physx::PxErrorCode code, const char *message, const char* file, int line ) +{ + UTF8 info[1024]; + dSprintf( info, 1024, "File: %s\nLine: %d\n%s", file, line, message ); + Platform::AlertOK( "PhysX Error", info ); + // Con::printf( "PhysX Error:\n %s(%d) : %s\n", file, line, message ); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Stream.h b/Engine/source/T3D/physics/physx3/px3Stream.h new file mode 100644 index 000000000..155165881 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Stream.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3STREAM_H_ +#define _T3D_PHYSICS_PX3STREAM_H_ + +#ifndef _PHYSX3_H_ +#include "T3D/physics/physx3/px3.h" +#endif +#ifndef _MEMSTREAM_H_ +#include "core/stream/memStream.h" +#endif + + +class Px3MemOutStream : public physx::PxOutputStream +{ +public: + + Px3MemOutStream(); + virtual ~Px3MemOutStream(); + + void resetPosition(); + + virtual physx::PxU32 write(const void *src, physx::PxU32 count); + physx::PxU32 getSize() const {return mMemStream.getStreamSize();} + physx::PxU8* getData() const {return (physx::PxU8*)mMemStream.getBuffer();} + +protected: + + mutable MemStream mMemStream; +}; + +class Px3MemInStream: public physx::PxInputData +{ + public: + Px3MemInStream(physx::PxU8* data, physx::PxU32 length); + virtual physx::PxU32 read(void* dest, physx::PxU32 count); + physx::PxU32 getLength() const; + virtual void seek(physx::PxU32 pos); + virtual physx::PxU32 tell() const; +protected: + mutable MemStream mMemStream; + + }; + +class Px3ConsoleStream : public physx::PxDefaultErrorCallback +{ +protected: + + virtual void reportError( physx::PxErrorCode code, const char *message, const char* file, int line ); + +public: + + Px3ConsoleStream(); + virtual ~Px3ConsoleStream(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Utils.cpp b/Engine/source/T3D/physics/physx3/px3Utils.cpp new file mode 100644 index 000000000..ced52198c --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Utils.cpp @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Utils.h" +#include "T3D/physics/physx3/px3.h" + +physx::PxShape* px3GetFirstShape(physx::PxRigidActor *actor) +{ + physx::PxShape *shapes[1]; + actor->getShapes(shapes, 1); + return shapes[0]; +} diff --git a/Engine/source/T3D/physics/physx3/px3Utils.h b/Engine/source/T3D/physics/physx3/px3Utils.h new file mode 100644 index 000000000..d80b5acce --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Utils.h @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_UTILS_H_ +#define _PHYSX3_UTILS_H_ + +namespace physx +{ + class PxRigidActor; + class PxShape; +} + +extern physx::PxShape* px3GetFirstShape(physx::PxRigidActor *actor); + + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3World.cpp b/Engine/source/T3D/physics/physx3/px3World.cpp new file mode 100644 index 000000000..67fd78cf0 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3World.cpp @@ -0,0 +1,567 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3World.h" + +#include "T3D/physics/physx3/px3.h" +#include "T3D/physics/physx3/px3Plugin.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3Stream.h" +#include "T3D/physics/physicsUserData.h" + +#include "console/engineAPI.h" +#include "core/stream/bitStream.h" +#include "platform/profiler.h" +#include "sim/netConnection.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/util/safeDelete.h" +#include "collision/collision.h" +#include "T3D/gameBase/gameProcess.h" +#include "gfx/sim/debugDraw.h" +#include "gfx/primBuilder.h" + + +physx::PxPhysics* gPhysics3SDK = NULL; +physx::PxCooking* Px3World::smCooking = NULL; +physx::PxFoundation* Px3World::smFoundation = NULL; +physx::PxProfileZoneManager* Px3World::smProfileZoneManager = NULL; +physx::PxDefaultCpuDispatcher* Px3World::smCpuDispatcher=NULL; +Px3ConsoleStream* Px3World::smErrorCallback = NULL; +physx::PxVisualDebuggerConnection* Px3World::smPvdConnection=NULL; +physx::PxDefaultAllocator Px3World::smMemoryAlloc; +//Physics timing +F32 Px3World::smPhysicsStepTime = 1.0f/(F32)TickMs; +U32 Px3World::smPhysicsMaxIterations = 4; + +Px3World::Px3World(): mScene( NULL ), + mProcessList( NULL ), + mIsSimulating( false ), + mErrorReport( false ), + mTickCount( 0 ), + mIsEnabled( false ), + mEditorTimeScale( 1.0f ), + mAccumulator( 0 ), + mControllerManager( NULL ) +{ +} + +Px3World::~Px3World() +{ +} + +physx::PxCooking *Px3World::getCooking() +{ + return smCooking; +} + +void Px3World::setTiming(F32 stepTime,U32 maxIterations) +{ + smPhysicsStepTime = stepTime; + smPhysicsMaxIterations = maxIterations; +} + +bool Px3World::restartSDK( bool destroyOnly, Px3World *clientWorld, Px3World *serverWorld) +{ + // If either the client or the server still exist + // then we cannot reset the SDK. + if ( clientWorld || serverWorld ) + return false; + + if(smPvdConnection) + smPvdConnection->release(); + + if(smCooking) + smCooking->release(); + + if(smCpuDispatcher) + smCpuDispatcher->release(); + + // Destroy the existing SDK. + if ( gPhysics3SDK ) + { + PxCloseExtensions(); + gPhysics3SDK->release(); + } + + if(smErrorCallback) + { + SAFE_DELETE(smErrorCallback); + } + + if(smFoundation) + { + smFoundation->release(); + SAFE_DELETE(smErrorCallback); + } + + // If we're not supposed to restart... return. + if ( destroyOnly ) + return true; + + bool memTrack = false; + #ifdef TORQUE_DEBUG + memTrack = true; + #endif + + smErrorCallback = new Px3ConsoleStream; + smFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, smMemoryAlloc, *smErrorCallback); + smProfileZoneManager = &physx::PxProfileZoneManager::createProfileZoneManager(smFoundation); + gPhysics3SDK = PxCreatePhysics(PX_PHYSICS_VERSION, *smFoundation, physx::PxTolerancesScale(),memTrack,smProfileZoneManager); + + if ( !gPhysics3SDK ) + { + Con::errorf( "PhysX3 failed to initialize!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + avar("PhysX3 could not be started!\r\n"), + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + + // We shouldn't get here, but this shuts up + // source diagnostic tools. + return false; + } + + if(!PxInitExtensions(*gPhysics3SDK)) + { + Con::errorf( "PhysX3 failed to initialize extensions!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + avar("PhysX3 could not be started!\r\n"), + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + return false; + } + + smCooking = PxCreateCooking(PX_PHYSICS_VERSION, *smFoundation, physx::PxCookingParams(physx::PxTolerancesScale())); + if(!smCooking) + { + Con::errorf( "PhysX3 failed to initialize cooking!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + avar("PhysX3 could not be started!\r\n"), + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + return false; + } + + //just for testing-must remove, should really be enabled via console like physx 2 plugin +#ifdef TORQUE_DEBUG + physx::PxVisualDebuggerConnectionFlags connectionFlags(physx::PxVisualDebuggerExt::getAllConnectionFlags()); + smPvdConnection = physx::PxVisualDebuggerExt::createConnection(gPhysics3SDK->getPvdConnectionManager(), + "localhost", 5425, 100, connectionFlags); +#endif + + return true; +} + +void Px3World::destroyWorld() +{ + getPhysicsResults(); + + // Release the tick processing signals. + if ( mProcessList ) + { + mProcessList->preTickSignal().remove( this, &Px3World::getPhysicsResults ); + mProcessList->postTickSignal().remove( this, &Px3World::tickPhysics ); + mProcessList = NULL; + } + + if(mControllerManager) + { + mControllerManager->release(); + mControllerManager = NULL; + } + + // Destroy the scene. + if ( mScene ) + { + // Release the scene. + mScene->release(); + mScene = NULL; + } + +} + +bool Px3World::initWorld( bool isServer, ProcessList *processList ) +{ + if ( !gPhysics3SDK ) + { + Con::errorf( "Physx3World::init - PhysXSDK not initialized!" ); + return false; + } + + mIsServer = isServer; + + physx::PxSceneDesc sceneDesc(gPhysics3SDK->getTolerancesScale()); + + sceneDesc.gravity = px3Cast(mGravity); + sceneDesc.userData = this; + if(!sceneDesc.cpuDispatcher) + { + smCpuDispatcher = physx::PxDefaultCpuDispatcherCreate(PHYSICSMGR->getThreadCount()); + sceneDesc.cpuDispatcher = smCpuDispatcher; + Con::printf("PhysX3 using Cpu: %d workers", smCpuDispatcher->getWorkerCount()); + } + + sceneDesc.flags |= physx::PxSceneFlag::eENABLE_ACTIVETRANSFORMS; + + sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader; + + mScene = gPhysics3SDK->createScene(sceneDesc); + + physx::PxDominanceGroupPair debrisDominance( 0.0f, 1.0f ); + mScene->setDominanceGroupPair(0,31,debrisDominance); + + mControllerManager = PxCreateControllerManager(*mScene); + + AssertFatal( processList, "Px3World::init() - We need a process list to create the world!" ); + mProcessList = processList; + mProcessList->preTickSignal().notify( this, &Px3World::getPhysicsResults ); + mProcessList->postTickSignal().notify( this, &Px3World::tickPhysics, 1000.0f ); + + return true; +} +// Most of this borrowed from bullet physics library, see btDiscreteDynamicsWorld.cpp +bool Px3World::_simulate(const F32 dt) +{ + int numSimulationSubSteps = 0; + //fixed timestep with interpolation + mAccumulator += dt; + if (mAccumulator >= smPhysicsStepTime) + { + numSimulationSubSteps = int(mAccumulator / smPhysicsStepTime); + mAccumulator -= numSimulationSubSteps * smPhysicsStepTime; + } + if (numSimulationSubSteps) + { + //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt + int clampedSimulationSteps = (numSimulationSubSteps > smPhysicsMaxIterations)? smPhysicsMaxIterations : numSimulationSubSteps; + + for (int i=0;ifetchResults(true); + mScene->simulate(smPhysicsStepTime); + } + } + + mIsSimulating = true; + + return true; +} + +void Px3World::tickPhysics( U32 elapsedMs ) +{ + if ( !mScene || !mIsEnabled ) + return; + + // Did we forget to call getPhysicsResults somewhere? + AssertFatal( !mIsSimulating, "PhysX3World::tickPhysics() - Already simulating!" ); + + // The elapsed time should be non-zero and + // a multiple of TickMs! + AssertFatal( elapsedMs != 0 && + ( elapsedMs % TickMs ) == 0 , "PhysX3World::tickPhysics() - Got bad elapsed time!" ); + + PROFILE_SCOPE(Px3World_TickPhysics); + + // Convert it to seconds. + const F32 elapsedSec = (F32)elapsedMs * 0.001f; + mIsSimulating = _simulate(elapsedSec * mEditorTimeScale); + + //Con::printf( "%s PhysX3World::tickPhysics!", mIsServer ? "Client" : "Server" ); +} + +void Px3World::getPhysicsResults() +{ + if ( !mScene || !mIsSimulating ) + return; + + PROFILE_SCOPE(Px3World_GetPhysicsResults); + + // Get results from scene. + mScene->fetchResults(true); + mIsSimulating = false; + mTickCount++; + + // Con::printf( "%s PhysXWorld::getPhysicsResults!", this == smClientWorld ? "Client" : "Server" ); +} + +void Px3World::releaseWriteLocks() +{ + Px3World *world = dynamic_cast( PHYSICSMGR->getWorld( "server" ) ); + + if ( world ) + world->releaseWriteLock(); + + world = dynamic_cast( PHYSICSMGR->getWorld( "client" ) ); + + if ( world ) + world->releaseWriteLock(); +} + +void Px3World::releaseWriteLock() +{ + if ( !mScene || !mIsSimulating ) + return; + + PROFILE_SCOPE(PxWorld_ReleaseWriteLock); + + // We use checkResults here to release the write lock + // but we do not change the simulation flag or increment + // the tick count... we may have gotten results, but the + // simulation hasn't really ticked! + mScene->checkResults( true ); + //AssertFatal( mScene->isWritable(), "PhysX3World::releaseWriteLock() - We should have been writable now!" ); +} + +bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ) +{ + + physx::PxVec3 orig = px3Cast( startPnt ); + physx::PxVec3 dir = px3Cast( endPnt - startPnt ); + physx::PxF32 maxDist = dir.magnitude(); + dir.normalize(); + + U32 groups = 0xffffffff; + groups &= ~( PX3_TRIGGER ); // No trigger shapes! + + physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::eIMPACT | physx::PxHitFlag::eNORMAL); + physx::PxQueryFilterData filterData(physx::PxQueryFlag::eSTATIC|physx::PxQueryFlag::eDYNAMIC); + filterData.data.word0 = groups; + physx::PxRaycastBuffer buf; + + if(!mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData)) + return false; + if(!buf.hasBlock) + return false; + + const physx::PxRaycastHit hit = buf.block; + physx::PxRigidActor *actor = hit.actor; + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + if ( ri ) + { + ri->object = ( userData != NULL ) ? userData->getObject() : NULL; + + if ( ri->object == NULL ) + + ri->distance = hit.distance; + ri->normal = px3Cast( hit.normal ); + ri->point = px3Cast( hit.position ); + ri->t = maxDist / hit.distance; + } + + if ( impulse.isZero() || + !actor->isRigidDynamic() || + actor->is()->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC ) + return true; + + physx::PxRigidBody *body = actor->is(); + physx::PxVec3 force = px3Cast( impulse ); + physx::PxRigidBodyExt::addForceAtPos(*body,force,hit.position,physx::PxForceMode::eIMPULSE); + + return true; +} + +PhysicsBody* Px3World::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes ) +{ + physx::PxVec3 orig = px3Cast( start ); + physx::PxVec3 dir = px3Cast( end - start ); + physx::PxF32 maxDist = dir.magnitude(); + dir.normalize(); + + U32 groups = 0xFFFFFFFF; + if ( !( bodyTypes & BT_Player ) ) + groups &= ~( PX3_PLAYER ); + + // TODO: For now always skip triggers and debris, + // but we should consider how game specifc this API + // should be in the future. + groups &= ~( PX3_TRIGGER ); // triggers + groups &= ~( PX3_DEBRIS ); // debris + + physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::eIMPACT | physx::PxHitFlag::eNORMAL); + physx::PxQueryFilterData filterData; + if(bodyTypes & BT_Static) + filterData.flags |= physx::PxQueryFlag::eSTATIC; + if(bodyTypes & BT_Dynamic) + filterData.flags |= physx::PxQueryFlag::eDYNAMIC; + + filterData.data.word0 = groups; + physx::PxRaycastBuffer buf; + + if( !mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData) ) + return NULL; + if(!buf.hasBlock) + return NULL; + + physx::PxRigidActor *actor = buf.block.actor; + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + if( !userData ) + return NULL; + + return userData->getBody(); +} + +void Px3World::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ) +{ + physx::PxVec3 nxPos = px3Cast( pos ); + const physx::PxU32 bufferSize = 10; + physx::PxSphereGeometry worldSphere(radius); + physx::PxTransform pose(nxPos); + physx::PxOverlapBufferN buffer; + + if(!mScene->overlap(worldSphere,pose,buffer)) + return; + + for ( physx::PxU32 i = 0; i < buffer.nbTouches; i++ ) + { + physx::PxRigidActor *actor = buffer.touches[i].actor; + + bool dynamic = actor->isRigidDynamic(); + + if ( !dynamic ) + continue; + + bool kinematic = actor->is()->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC; + + if ( kinematic ) + continue; + + physx::PxVec3 force = actor->getGlobalPose().p - nxPos; + force.normalize(); + force *= forceMagnitude; + + physx::PxRigidBody *body = actor->is(); + physx::PxRigidBodyExt::addForceAtPos(*body,force,nxPos,physx::PxForceMode::eIMPULSE); + } +} + +void Px3World::setEnabled( bool enabled ) +{ + mIsEnabled = enabled; + + if ( !mIsEnabled ) + getPhysicsResults(); +} + +physx::PxController* Px3World::createController( physx::PxControllerDesc &desc ) +{ + if ( !mScene ) + return NULL; + + // We need the writelock! + releaseWriteLock(); + physx::PxController* pController = mControllerManager->createController(desc); + AssertFatal( pController, "Px3World::createController - Got a null!" ); + return pController; +} + +static ColorI getDebugColor( physx::PxU32 packed ) +{ + ColorI col; + col.blue = (packed)&0xff; + col.green = (packed>>8)&0xff; + col.red = (packed>>16)&0xff; + col.alpha = 255; + + return col; +} + +void Px3World::onDebugDraw( const SceneRenderState *state ) +{ + if ( !mScene ) + return; + + mScene->setVisualizationParameter(physx::PxVisualizationParameter::eSCALE,1.0f); + mScene->setVisualizationParameter(physx::PxVisualizationParameter::eBODY_AXES,1.0f); + mScene->setVisualizationParameter(physx::PxVisualizationParameter::eCOLLISION_SHAPES,1.0f); + + const physx::PxRenderBuffer *renderBuffer = &mScene->getRenderBuffer(); + + if(!renderBuffer) + return; + + // Render points + { + physx::PxU32 numPoints = renderBuffer->getNbPoints(); + const physx::PxDebugPoint *points = renderBuffer->getPoints(); + + PrimBuild::begin( GFXPointList, numPoints ); + + while ( numPoints-- ) + { + PrimBuild::color( getDebugColor(points->color) ); + PrimBuild::vertex3fv(px3Cast(points->pos)); + points++; + } + + PrimBuild::end(); + } + + // Render lines + { + physx::PxU32 numLines = renderBuffer->getNbLines(); + const physx::PxDebugLine *lines = renderBuffer->getLines(); + + PrimBuild::begin( GFXLineList, numLines * 2 ); + + while ( numLines-- ) + { + PrimBuild::color( getDebugColor( lines->color0 ) ); + PrimBuild::vertex3fv( px3Cast(lines->pos0)); + PrimBuild::color( getDebugColor( lines->color1 ) ); + PrimBuild::vertex3fv( px3Cast(lines->pos1)); + lines++; + } + + PrimBuild::end(); + } + + // Render triangles + { + physx::PxU32 numTris = renderBuffer->getNbTriangles(); + const physx::PxDebugTriangle *triangles = renderBuffer->getTriangles(); + + PrimBuild::begin( GFXTriangleList, numTris * 3 ); + + while ( numTris-- ) + { + PrimBuild::color( getDebugColor( triangles->color0 ) ); + PrimBuild::vertex3fv( px3Cast(triangles->pos0) ); + PrimBuild::color( getDebugColor( triangles->color1 ) ); + PrimBuild::vertex3fv( px3Cast(triangles->pos1)); + PrimBuild::color( getDebugColor( triangles->color2 ) ); + PrimBuild::vertex3fv( px3Cast(triangles->pos2) ); + triangles++; + } + + PrimBuild::end(); + } + +} + +//set simulation timing via script +DefineEngineFunction( physx3SetSimulationTiming, void, ( F32 stepTime, U32 maxSteps ),, "Set simulation timing of the PhysX 3 plugin" ) +{ + Px3World::setTiming(stepTime,maxSteps); +} diff --git a/Engine/source/T3D/physics/physx3/px3World.h b/Engine/source/T3D/physics/physx3/px3World.h new file mode 100644 index 000000000..faf51a41a --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3World.h @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_WORLD_H_ +#define _PHYSX3_WORLD_H_ + +#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_ +#include "T3D/physics/physicsWorld.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _PHYSX3_H_ +#include "T3D/physics/physx3/px3.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class Px3ConsoleStream; +class Px3ContactReporter; +class FixedStepper; + +enum Px3CollisionGroup +{ + PX3_DEFAULT = BIT(0), + PX3_PLAYER = BIT(1), + PX3_DEBRIS = BIT(2), + PX3_TRIGGER = BIT(3), +}; + +class Px3World : public PhysicsWorld +{ +protected: + + physx::PxScene* mScene; + + bool mIsEnabled; + bool mIsSimulating; + bool mIsServer; + U32 mTickCount; + ProcessList *mProcessList; + F32 mEditorTimeScale; + bool mErrorReport; + physx::PxControllerManager* mControllerManager; + static Px3ConsoleStream *smErrorCallback; + static physx::PxDefaultAllocator smMemoryAlloc; + static physx::PxFoundation* smFoundation; + static physx::PxCooking *smCooking; + static physx::PxProfileZoneManager* smProfileZoneManager; + static physx::PxDefaultCpuDispatcher* smCpuDispatcher; + static physx::PxVisualDebuggerConnection* smPvdConnection; + static F32 smPhysicsStepTime; + static U32 smPhysicsMaxIterations; + + F32 mAccumulator; + bool _simulate(const F32 dt); + +public: + + Px3World(); + virtual ~Px3World(); + + virtual bool initWorld( bool isServer, ProcessList *processList ); + virtual void destroyWorld(); + virtual void onDebugDraw( const SceneRenderState *state ); + virtual void reset() {} + virtual bool castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ); + virtual PhysicsBody* castRay( const Point3F &start, const Point3F &end, U32 bodyTypes = BT_All ); + virtual void explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ); + virtual bool isEnabled() const { return mIsEnabled; } + physx::PxScene* getScene(){ return mScene;} + void setEnabled( bool enabled ); + U32 getTick() { return mTickCount; } + void tickPhysics( U32 elapsedMs ); + void getPhysicsResults(); + void setEditorTimeScale( F32 timeScale ) { mEditorTimeScale = timeScale; } + const F32 getEditorTimeScale() const { return mEditorTimeScale; } + void releaseWriteLock(); + bool isServer(){return mIsServer;} + physx::PxController* createController( physx::PxControllerDesc &desc ); + //static + static bool restartSDK( bool destroyOnly = false, Px3World *clientWorld = NULL, Px3World *serverWorld = NULL ); + static void releaseWriteLocks(); + static physx::PxCooking *getCooking(); + static void setTiming(F32 stepTime,U32 maxIterations); +}; + + + +#endif \ No newline at end of file diff --git a/Tools/projectGenerator/modules/physX3.inc b/Tools/projectGenerator/modules/physX3.inc new file mode 100644 index 000000000..42eb48b5a --- /dev/null +++ b/Tools/projectGenerator/modules/physX3.inc @@ -0,0 +1,106 @@ +