From 0600e76717e2cbeb03ddaec7abd680621a569fc6 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 8 Sep 2023 00:28:53 +0400 Subject: [PATCH] WIP --- common/port.h | 4 +- game_shared/matrix.h | 2 +- game_shared/vector.cpp | 14 +- game_shared/vector.h | 19 +- server/CMakeLists.txt | 39 +- server/cbase.cpp | 5 +- server/cbase.h | 10 +- server/novodex.cpp | 1537 +++++++++++-------------- server/novodex.h | 210 ++-- server/physic.h | 11 +- server/physics/NxErrorStream.cpp | 54 + server/physics/NxErrorStream.h | 49 +- server/physics/NxUserStream.cpp | 193 +--- server/physics/NxUserStream.h | 64 +- server/physics/contact_report.cpp | 71 ++ server/physics/contact_report.h | 35 + server/physics/debug_renderer.cpp | 84 ++ server/physics/debug_renderer.h | 35 + server/physics/triangulated_shape.cpp | 229 ++++ server/physics/triangulated_shape.h | 40 + server/util.h | 4 +- 21 files changed, 1448 insertions(+), 1261 deletions(-) create mode 100644 server/physics/NxErrorStream.cpp create mode 100644 server/physics/contact_report.cpp create mode 100644 server/physics/contact_report.h create mode 100644 server/physics/debug_renderer.cpp create mode 100644 server/physics/debug_renderer.h create mode 100644 server/physics/triangulated_shape.cpp create mode 100644 server/physics/triangulated_shape.h diff --git a/common/port.h b/common/port.h index 011ba1e0..0f7a3997 100644 --- a/common/port.h +++ b/common/port.h @@ -94,8 +94,8 @@ GNU General Public License for more details. #define FORCEINLINE __forceinline #endif - #define open _open - #define read _read + //#define open _open + //#define read _read #define alloca _alloca #define WIN32_LEAN_AND_MEAN diff --git a/game_shared/matrix.h b/game_shared/matrix.h index 8c40d380..c7ca175b 100644 --- a/game_shared/matrix.h +++ b/game_shared/matrix.h @@ -677,7 +677,7 @@ class matrix4x4 } // init from OpenGl matrix - matrix4x4( float *opengl_matrix ) + matrix4x4( const float *opengl_matrix ) { mat[0][0] = opengl_matrix[0]; mat[0][1] = opengl_matrix[1]; diff --git a/game_shared/vector.cpp b/game_shared/vector.cpp index 1774303d..bbeddae8 100644 --- a/game_shared/vector.cpp +++ b/game_shared/vector.cpp @@ -14,16 +14,22 @@ GNU General Public License for more details. */ #ifdef USE_PHYSICS_ENGINE #include "vector.h" -#include "NxVec3.h" +#include "PxVec3.h" -Vector :: Vector( const NxVec3& v ) +Vector::Vector(const physx::PxVec3& v) { x = v.x; y = v.y; z = v.z; } -const Vector& Vector :: operator = ( const NxVec3& v ) +const Vector& Vector :: operator = (const physx::PxVec3& v) { x = v.x; y = v.y; z = v.z; return *this; } -#endif \ No newline at end of file + +Vector::operator physx::PxVec3() const +{ + return physx::PxVec3(x, y, z); +} + +#endif diff --git a/game_shared/vector.h b/game_shared/vector.h index 896511bc..ca0c38cf 100644 --- a/game_shared/vector.h +++ b/game_shared/vector.h @@ -31,7 +31,9 @@ #pragma warning( disable : 4244 ) // disable 'possible loss of data converting float to int' warning message #pragma warning( disable : 4305 ) // disable 'truncation from 'const double' to 'float' warning message -class NxVec3; +namespace physx { + class PxVec3; +}; class Radian; inline void SinCos( float angle, float *sine, float *cosine ) @@ -126,8 +128,6 @@ typedef Vector2D vec2_t; inline float DotProduct(const Vector2D& a, const Vector2D& b) { return( a.x*b.x + a.y*b.y ); } inline Vector2D operator*(float fl, const Vector2D& v) { return v * fl; } -class NxVec3; - //========================================================= // 3D Vector //========================================================= @@ -141,7 +141,7 @@ class Vector // same data-layout as engine's vec3_t, inline Vector( const float *rgfl ) { x = rgfl[0]; y = rgfl[1]; z = rgfl[2]; } inline Vector(float rgfl[3]) { x = rgfl[0]; y = rgfl[1]; z = rgfl[2]; } inline Vector( float fill ) { x = fill; y = fill; z = fill; } - Vector(const NxVec3& v); + Vector(const physx::PxVec3& v); // Initialization void Init(float ix=0.0f, float iy=0.0f, float iz=0.0f){ x = ix; y = iy; z = iz; } @@ -157,8 +157,11 @@ class Vector // same data-layout as engine's vec3_t, inline Vector operator*(float fl) const { return Vector(x*fl, y*fl, z*fl); } inline Vector operator/(float fl) const { return Vector(x/fl, y/fl, z/fl); } inline Vector operator*(const Vector& v) const { return Vector(x*v.x, y*v.y, z*v.z); } - const Vector& operator=(const NxVec3& v); - + operator float *() { return &x; } // Vectors will now automatically convert to float * when needed + operator const float *() const { return &x; } + const Vector& operator=(const physx::PxVec3& v); + operator physx::PxVec3() const; + _forceinline Vector& operator+=(const Vector &v) { x+=v.x; y+=v.y; z += v.z; @@ -255,8 +258,6 @@ class Vector // same data-layout as engine's vec3_t, inline float Length(void) const { return sqrt( x*x + y*y + z*z ); } inline float LengthSqr(void) const { return (x*x + y*y + z*z); } inline float MaxCoord() const { return Q_max(x, Q_max(y, z)); } - operator float *() { return &x; } // Vectors will now automatically convert to float * when needed - operator const float *() const { return &x; } inline Vector Normalize() const { @@ -437,7 +438,7 @@ class Radian inline Radian operator*(float fl) const { return Radian(x*fl, y*fl, z*fl); } inline Radian operator/(float fl) const { return Radian(x/fl, y/fl, z/fl); } inline Radian operator*(const Radian& v) const { return Radian(x*v.x, y*v.y, z*v.z); } - const Radian& operator=(const NxVec3& v); + const Radian& operator=(const physx::PxVec3& v); _forceinline Radian& operator+=(const Radian &v) { diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 0b6d3755..ee322073 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -93,8 +93,6 @@ list(APPEND SVDLL_SOURCES "multiplay_gamerules.cpp" "monsters/nihilanth.cpp" "monsters/nodes.cpp" - "novodex.cpp" - "physics/NxUserStream.cpp" "monsters/osprey.cpp" "pathcorner.cpp" "physents.cpp" @@ -138,11 +136,22 @@ list(APPEND SVDLL_SOURCES "monsters/zombie.cpp" ) +if(ENABLE_PHYSX) + list(APPEND SVDLL_SOURCES + "novodex.cpp" + "physics/contact_report.cpp" + "physics/debug_renderer.cpp" + "physics/triangulated_shape.cpp" + "physics/NxUserStream.cpp" + "physics/NxErrorStream.cpp" + ) +endif() + # add .def file to sources if(MSVC) -list(APPEND SVDLL_SOURCES - "server.def" -) + list(APPEND SVDLL_SOURCES + "server.def" + ) endif() add_library (${PROJECT_NAME} SHARED ${SVDLL_SOURCES}) @@ -151,13 +160,21 @@ target_include_directories(${PROJECT_NAME} PRIVATE "monsters" "physics" "wpn_shared" - "${CMAKE_SOURCE_DIR}/external/novodex" + "${CMAKE_SOURCE_DIR}/external/physx/physx/include" + "${CMAKE_SOURCE_DIR}/external/physx/physx/include/cooking" + "${CMAKE_SOURCE_DIR}/external/physx/physx/include/geometry" + "${CMAKE_SOURCE_DIR}/external/physx/pxshared/include" + "${CMAKE_SOURCE_DIR}/external/physx/pxshared/include/foundation" "${CMAKE_SOURCE_DIR}/common" "${CMAKE_SOURCE_DIR}/engine" "${CMAKE_SOURCE_DIR}/game_shared" "${CMAKE_SOURCE_DIR}/public" ) +target_link_directories(${PROJECT_NAME} PRIVATE + "${CMAKE_SOURCE_DIR}/external/physx/bin/win.x86_64.vc142.mt/debug" +) + if(HAVE_TGMATH_H) target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_TGMATH_H=1) endif() @@ -199,6 +216,16 @@ if(ENABLE_STATIC_LINKING) set_compiler_runtime(${PROJECT_NAME} STATIC) endif() +target_link_libraries(${PROJECT_NAME} PRIVATE + PhysX_64 + PhysXCommon_64 + PhysXCooking_64 + PhysXExtensions_static_64 + PhysXFoundation_64 + PhysXPvdSDK_static_64 + PhysXVehicle_static_64 +) + set_target_properties (${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES diff --git a/server/cbase.cpp b/server/cbase.cpp index fc6bffb6..b71d07ac 100644 --- a/server/cbase.cpp +++ b/server/cbase.cpp @@ -1406,7 +1406,10 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FIELD( m_iActorType, FIELD_CHARACTER ), DEFINE_FIELD( m_iActorFlags, FIELD_INTEGER ), DEFINE_FIELD( m_iBodyFlags, FIELD_INTEGER ), - DEFINE_FIELD( m_usActorGroup, FIELD_SHORT ), + DEFINE_FIELD( m_iFilterData[0], FIELD_INTEGER ), + DEFINE_FIELD( m_iFilterData[1], FIELD_INTEGER ), + DEFINE_FIELD( m_iFilterData[2], FIELD_INTEGER ), + DEFINE_FIELD( m_iFilterData[3], FIELD_INTEGER ), DEFINE_FIELD( m_flBodyMass, FIELD_FLOAT ), DEFINE_FIELD( m_fFreezed, FIELD_BOOLEAN ), END_DATADESC() diff --git a/server/cbase.h b/server/cbase.h index 1313bf28..bcaba72f 100644 --- a/server/cbase.h +++ b/server/cbase.h @@ -25,8 +25,8 @@ CBaseEntity CBasePlayer CBaseGroup */ - -#define MAX_PATH_SIZE 10 // max number of nodes available for a path. +#pragma once +#define MAX_PATH_SIZE 10 // max number of nodes available for a path. // These are caps bits to indicate what an object's capabilities (currently used for save/restore and level transitions) #define FCAP_SET_MOVEDIR 0x00000001 // convert initial angles into direction (doors used) @@ -242,9 +242,9 @@ class CBaseEntity // PhysX description unsigned char m_iActorType; // static, kinetic or dynamic - int m_iActorFlags; // NxActor->flags - int m_iBodyFlags; // NxBodyDesc->flags - short m_usActorGroup; // NxActor->group + uint32_t m_iActorFlags; // NxActor->flags + uint32_t m_iBodyFlags; // NxBodyDesc->flags + uint32_t m_iFilterData[4]; // NxActor->group float m_flBodyMass; // NxActor->mass BOOL m_fFreezed; // is body sleeps? bool m_isChaining; diff --git a/server/novodex.cpp b/server/novodex.cpp index 00908d52..be095780 100644 --- a/server/novodex.cpp +++ b/server/novodex.cpp @@ -13,12 +13,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#include "physic.h" // must be first! #ifdef USE_PHYSICS_ENGINE - -#include "extdll.h" -#include "util.h" -#include "cbase.h" +#include "novodex.h" #include "saverestore.h" #include "client.h" #include "bspfile.h" @@ -31,134 +27,25 @@ GNU General Public License for more details. #include "trace.h" #include "game.h" #include "build.h" +#include "NxErrorStream.h" +#include "PxMat33.h" +#include "PxMat44.h" +#include "contact_report.h" +#include "debug_renderer.h" +#include "triangulated_shape.h" +#include +#include #if defined (HAS_PHYSIC_VEHICLE) #include "NxVehicle.h" #include "vehicles/NxParser.h" #endif +using namespace physx; + CPhysicNovodex NovodexPhysic; IPhysicLayer *WorldPhysic = &NovodexPhysic; -// exports given from physics SDK -static NxPhysicsSDK* (__cdecl *pNxCreatePhysicsSDK)( - NxU32 sdkVersion, - NxUserAllocator* allocator, - NxUserOutputStream* outputStream, - const NxPhysicsSDKDesc& desc, - NxSDKCreateError *errorCode -); -static NxCookingInterface *(__cdecl *pNxGetCookingLib)( NxU32 sdk_version_number ); -static void *(__cdecl *pNxReleasePhysicsSDK)( NxPhysicsSDK *sdk ); -static NxUtilLib* (__cdecl *pNxGetUtilLib)( void ); - -static dllfunc_t NxPhysics[] = -{ -{ "NxCreatePhysicsSDK", (void **)&pNxCreatePhysicsSDK }, -{ "NxReleasePhysicsSDK", (void **)&pNxReleasePhysicsSDK }, -{ "NxGetCookingLib", (void **)&pNxGetCookingLib }, -{ "NxGetUtilLib", (void **)&pNxGetUtilLib }, -{ NULL, NULL }, -}; - -static dllhandle_t hPhysics = NULL; - -class DebugRenderer -{ -public: - NX_INLINE void setupColor( NxU32 color ) const - { - NxF32 Blue = NxF32((color) & 0xff) / 255.0f; - NxF32 Green = NxF32((color>>8) & 0xff) / 255.0f; - NxF32 Red = NxF32((color>>16) & 0xff) / 255.0f; - Tri->Color4f( Red, Green, Blue, 1.0f ); - } - - void renderData( const NxDebugRenderable& data ) const - { - // Render points - NxU32 NbPoints = data.getNbPoints(); - const NxDebugPoint* Points = data.getPoints(); - - Tri->Begin( TRI_POINTS ); - while( NbPoints-- ) - { - setupColor( Points->color ); - Tri->Vertex3fv( (float *)&Points->p.x ); - Points++; - } - Tri->End(); - - // Render lines - NxU32 NbLines = data.getNbLines(); - const NxDebugLine* Lines = data.getLines(); - - Tri->Begin( TRI_LINES ); - while( NbLines-- ) - { - setupColor( Lines->color ); - Tri->Vertex3fv( (float *)&Lines->p0.x ); - Tri->Vertex3fv( (float *)&Lines->p1.x ); - Lines++; - } - Tri->End(); - - // Render triangles - NxU32 NbTris = data.getNbTriangles(); - const NxDebugTriangle* Triangles = data.getTriangles(); - - Tri->Begin( TRI_TRIANGLES ); - while( NbTris-- ) - { - setupColor( Triangles->color ); - Tri->Vertex3fv( (float *)&Triangles->p0.x ); - Tri->Vertex3fv( (float *)&Triangles->p1.x ); - Tri->Vertex3fv( (float *)&Triangles->p2.x ); - Triangles++; - } - Tri->End(); - } -} gDebugRenderer; - -class ContactReport : public NxUserContactReport -{ -public: - virtual void onContactNotify( NxContactPair& pair, NxU32 events ) - { - if( !FBitSet( events, NX_NOTIFY_ON_TOUCH )) - return; - - edict_t *e1 = (edict_t *)pair.actors[0]->userData; - edict_t *e2 = (edict_t *)pair.actors[1]->userData; - - if( !e1 || !e2 ) return; - - if( e1->v.flags & FL_CONVEYOR ) - { - Vector basevelocity = e1->v.movedir * e1->v.speed * CONVEYOR_SCALE_FACTOR; - pair.actors[1]->setLinearMomentum( basevelocity ); - } - - if( e2->v.flags & FL_CONVEYOR ) - { - Vector basevelocity = e2->v.movedir * e2->v.speed * CONVEYOR_SCALE_FACTOR; - pair.actors[0]->setLinearMomentum( basevelocity ); - } - - if( e1 && e1->v.solid != SOLID_NOT ) - { - // FIXME: build trace info - DispatchTouch( e1, e2 ); - } - - if( e2 && e2->v.solid != SOLID_NOT ) - { - // FIXME: build trace info - DispatchTouch( e1, e2 ); - } - } -} gContactReport; - void CPhysicNovodex :: InitPhysic( void ) { if( m_pPhysics ) @@ -174,122 +61,112 @@ void CPhysicNovodex :: InitPhysic( void ) return; } -#if !XASH_64BIT - const char *libraryName = "PhysXLoader.dll"; - const char *libraryGlobalName = "*PhysXLoader.dll"; -#else - const char *libraryName = "PhysXLoader64.dll"; - const char *libraryGlobalName = "*PhysXLoader64.dll"; -#endif - - // trying to load dlls from mod-folder - if (!Sys_LoadLibrary(libraryName, &hPhysics, NxPhysics)) + m_pFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, m_Allocator, m_ErrorCallback); + if (!m_pFoundation) { - // NOTE: using '*' symbol to force loading dll from system path not game folder (Nvidia PhysX drivers) - if (!Sys_LoadLibrary(libraryGlobalName, &hPhysics, NxPhysics)) - { - ALERT(at_error, "InitPhysic: failed to loading \"%s\"\nPhysics abstraction layer will be disabled.\n", libraryName); - GameInitNullPhysics(); - return; - } + ALERT(at_error, "InitPhysic: failed to create foundation\n"); + GameInitNullPhysics(); + return; } - m_pPhysics = pNxCreatePhysicsSDK( NX_PHYSICS_SDK_VERSION, NULL, &m_ErrorStream, NxPhysicsSDKDesc(), NULL ); + PxTolerancesScale scale; + scale.length = 39.3701 * 1.76; // typical length of an object + scale.speed = 800.0; // typical speed of an object, gravity*1s is a reasonable choice + + m_pVisualDebugger = PxCreatePvd(*m_pFoundation); + PxPvdTransport *transport = PxDefaultPvdSocketTransportCreate("127.0.0.1", 5425, 1000); + m_pVisualDebugger->connect(*transport, PxPvdInstrumentationFlag::eALL); - if( !m_pPhysics ) + m_pPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *m_pFoundation, scale, false, m_pVisualDebugger); + m_pDispatcher = PxDefaultCpuDispatcherCreate(std::thread::hardware_concurrency()); + if (!m_pPhysics) { ALERT( at_error, "InitPhysic: failed to initalize physics engine\n" ); - Sys_FreeLibrary( &hPhysics ); - GameInitNullPhysics (); + GameInitNullPhysics(); return; } - m_pCooking = pNxGetCookingLib( NX_PHYSICS_SDK_VERSION ); - - if( !m_pCooking ) + m_pCooking = PxCreateCooking(PX_PHYSICS_VERSION, *m_pFoundation, PxCookingParams(scale)); + if (!m_pCooking) { ALERT( at_warning, "InitPhysic: failed to initalize cooking library\n" ); } - m_pUtils = pNxGetUtilLib(); - - if( !m_pUtils ) - { - ALERT( at_warning, "InitPhysic: failed to initalize util library\n" ); + if (!PxInitExtensions(*m_pPhysics, m_pVisualDebugger)) { + ALERT( at_warning, "InitPhysic: failed to initalize extensions\n" ); } - float maxSpeed = CVAR_GET_FLOAT( "sv_maxspeed" ); - - m_pPhysics->setParameter( NX_SKIN_WIDTH, 0.25f ); - - m_pPhysics->setParameter( NX_VISUALIZATION_SCALE, 1.0f ); - m_pPhysics->setParameter( NX_VISUALIZE_COLLISION_SHAPES, 1 ); - m_pPhysics->setParameter( NX_VISUALIZE_CONTACT_POINT, 1 ); - m_pPhysics->setParameter( NX_VISUALIZE_CONTACT_NORMAL, 1 ); - m_pPhysics->setParameter( NX_MAX_ANGULAR_VELOCITY, maxSpeed ); - m_pPhysics->setParameter( NX_CONTINUOUS_CD, 1 ); - m_pPhysics->setParameter( NX_VISUALIZE_BODY_AXES, 1 ); - m_pPhysics->setParameter( NX_DEFAULT_SLEEP_LIN_VEL_SQUARED, 5.0f ); - m_pPhysics->setParameter( NX_DEFAULT_SLEEP_ANG_VEL_SQUARED, 5.0f ); - m_pPhysics->setParameter( NX_VISUALIZE_FORCE_FIELDS, 1.0f ); - m_pPhysics->setParameter( NX_ADAPTIVE_FORCE, 0.0f ); - // create a scene - NxSceneDesc sceneDesc; - - sceneDesc.userContactReport = &gContactReport; - sceneDesc.gravity = NxVec3( 0.0f, 0.0f, -800.0f ); - sceneDesc.maxTimestep = (1.0f / 150.0f); - sceneDesc.bpType = NX_BP_TYPE_SAP_MULTI; - sceneDesc.maxIter = SOLVER_ITERATION_COUNT; - sceneDesc.dynamicStructure = NX_PRUNING_DYNAMIC_AABB_TREE; - - worldBounds.min = NxVec3( -32768, -32768, -32768 ); - worldBounds.max = NxVec3( 32768, 32768, 32768 ); - sceneDesc.maxBounds = &worldBounds; - sceneDesc.nbGridCellsX = 8; - sceneDesc.nbGridCellsY = 8; - sceneDesc.upAxis = 2; - - m_pScene = m_pPhysics->createScene( sceneDesc ); - - // notify on all contacts: - m_pScene->setActorGroupPairFlags( 0, 0, NX_NOTIFY_ON_TOUCH ); - - NxMaterial *defaultMaterial = m_pScene->getMaterialFromIndex( 0 ); - defaultMaterial->setStaticFriction( 0.5f ); - defaultMaterial->setDynamicFriction( 0.5f ); - defaultMaterial->setRestitution( 0.0f ); - - NxMaterialDesc conveyorMat; - NxMaterial *conveyorMaterial = m_pScene->createMaterial( conveyorMat ); - conveyorMaterial->setStaticFriction( 1.0f ); - conveyorMaterial->setDynamicFriction( 1.0f ); - conveyorMaterial->setRestitution( 0.0f ); - conveyorMaterial->setDirOfAnisotropy( NxVec3( 0, 0, 1 )); - conveyorMaterial->setFlags( NX_MF_ANISOTROPIC ); - + PxSceneDesc sceneDesc(scale); + sceneDesc.simulationEventCallback = &ContactReport::getInstance(); + sceneDesc.gravity = PxVec3(0.0f, 0.0f, -800.0f); + //sceneDesc.bounceThresholdVelocity = 0.2 * scale.speed; + sceneDesc.flags = PxSceneFlag::eENABLE_CCD; + //sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP; + //sceneDesc.maxIter = SOLVER_ITERATION_COUNT; + //sceneDesc.dynamicStructure = PxPruningStructureType::eDYNAMIC_AABB_TREE; + sceneDesc.cpuDispatcher = m_pDispatcher; + sceneDesc.filterShader = []( + PxFilterObjectAttributes attributes0, PxFilterData filterData0, + PxFilterObjectAttributes attributes1, PxFilterData filterData1, + PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize) -> PxFilterFlags { + pairFlags = PxPairFlag::eCONTACT_DEFAULT + | PxPairFlag::eDETECT_CCD_CONTACT + | PxPairFlag::eNOTIFY_TOUCH_CCD + | PxPairFlag::eNOTIFY_TOUCH_FOUND + | PxPairFlag::eNOTIFY_TOUCH_PERSISTS + | PxPairFlag::eNOTIFY_CONTACT_POINTS + | PxPairFlag::eCONTACT_EVENT_POSE; + return PxFilterFlag::eDEFAULT; + }; + + m_worldBounds.minimum = PxVec3(-32768, -32768, -32768); + m_worldBounds.maximum = PxVec3(32768, 32768, 32768); + sceneDesc.sanityBounds = m_worldBounds; + + m_pScene = m_pPhysics->createScene(sceneDesc); + m_pScene->setVisualizationParameter(PxVisualizationParameter::eSCALE, 1.0f); // disable on release build because performance impact + m_pScene->setVisualizationParameter(PxVisualizationParameter::eCOLLISION_SHAPES, 1.0f); + m_pScene->setVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT, 1.0f); + m_pScene->setVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL, 1.0f); + m_pScene->setVisualizationParameter(PxVisualizationParameter::eBODY_AXES, 1.0f); + + PxPvdSceneClient *pvdClient = m_pScene->getScenePvdClient(); + if (pvdClient) + { + pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true); + pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true); + pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true); + } + + m_pDefaultMaterial = m_pPhysics->createMaterial(0.5f, 0.5f, 0.0f); + m_pConveyorMaterial = m_pPhysics->createMaterial(1.0f, 1.0f, 0.0f); m_fNeedFetchResults = FALSE; } void CPhysicNovodex :: FreePhysic( void ) { - if( !m_pPhysics ) return; - - if( m_pCooking ) - m_pCooking->NxCloseCooking(); - - m_pPhysics->releaseScene( *m_pScene ); - pNxReleasePhysicsSDK( m_pPhysics ); + if (m_pScene) m_pScene->release(); + if (m_pCooking) m_pCooking->release(); + if (m_pDispatcher) m_pDispatcher->release(); + if (m_pPhysics) m_pPhysics->release(); + if (m_pVisualDebugger) + { + PxPvdTransport *transport = m_pVisualDebugger->getTransport(); + m_pVisualDebugger->release(); + if (transport) { + transport->release(); + } + } - Sys_FreeLibrary( &hPhysics ); - m_pPhysics = NULL; - m_pScene = NULL; -} + PxCloseExtensions(); + if (m_pFoundation) m_pFoundation->release(); -void *CPhysicNovodex :: GetUtilLibrary( void ) -{ - return (void *)m_pUtils; + m_pScene = nullptr; + m_pCooking = nullptr; + m_pPhysics = nullptr; + m_pVisualDebugger = nullptr; + m_pFoundation = nullptr; } void CPhysicNovodex :: Update( float flTime ) @@ -297,6 +174,7 @@ void CPhysicNovodex :: Update( float flTime ) if( !m_pScene || GET_SERVER_STATE() != SERVER_ACTIVE ) return; + const float timeDelta = Q_min(flTime, 1.0f / 20.f); if( g_psv_gravity ) { // clamp gravity @@ -305,9 +183,7 @@ void CPhysicNovodex :: Update( float flTime ) if( g_psv_gravity->value > 800.0f ) CVAR_SET_FLOAT( "sv_gravity", 800.0f ); - NxVec3 gravity; - m_pScene->getGravity( gravity ); - + PxVec3 gravity = m_pScene->getGravity(); if( gravity.z != -( g_psv_gravity->value )) { ALERT( at_aiconsole, "gravity changed from %g to %g\n", gravity.z, -(g_psv_gravity->value)); @@ -318,16 +194,16 @@ void CPhysicNovodex :: Update( float flTime ) if( g_sync_physic.value ) { - m_pScene->simulate( flTime ); - m_pScene->flushStream(); - m_pScene->fetchResults( NX_RIGID_BODY_FINISHED, true ); + m_pScene->simulate( timeDelta ); + //m_pScene->flushStream(); + m_pScene->fetchResults( true ); } else { if( m_fNeedFetchResults ) return; // waiting - m_pScene->simulate( flTime ); + m_pScene->simulate( timeDelta ); m_fNeedFetchResults = TRUE; } } @@ -339,8 +215,8 @@ void CPhysicNovodex :: EndFrame( void ) if( m_fNeedFetchResults ) { - m_pScene->flushStream(); - m_pScene->fetchResults( NX_RIGID_BODY_FINISHED, true ); + //m_pScene->flushStream(); + m_pScene->fetchResults( true ); m_fNeedFetchResults = FALSE; } @@ -348,31 +224,36 @@ void CPhysicNovodex :: EndFrame( void ) if( !p_speeds || p_speeds->value <= 0.0f ) return; - NxSceneStats stats; - m_pScene->getStats( stats ); + PxSimulationStatistics stats; + m_pScene->getSimulationStatistics( stats ); switch( (int)p_speeds->value ) { case 1: - Q_snprintf( p_speeds_msg, sizeof( p_speeds_msg ), "%3i active bodies, %3i actors\n%3i static shapes, %3i dynamic shapes", - stats.numDynamicActorsInAwakeGroups, stats.numActors, stats.numStaticShapes, stats.numDynamicShapes ); + Q_snprintf(p_speeds_msg, sizeof(p_speeds_msg), + "%3i active dynamic bodies\n%3i static bodies\n%3i dynamic bodies", + stats.nbActiveDynamicBodies, + stats.nbStaticBodies, + stats.nbDynamicBodies + ); break; } } -void CPhysicNovodex :: RemoveBody( struct edict_s *pEdict ) +void CPhysicNovodex :: RemoveBody( edict_t *pEdict ) { if( !m_pScene || !pEdict || pEdict->free ) return; // scene purge all the objects automatically CBaseEntity *pEntity = CBaseEntity::Instance( pEdict ); - NxActor *pActor = ActorFromEntity( pEntity ); + PxActor *pActor = ActorFromEntity( pEntity ); - if( pActor ) m_pScene->releaseActor( *pActor ); + if( pActor ) + pActor->release(); pEntity->m_pUserData = NULL; } -NxConvexMesh *CPhysicNovodex :: ConvexMeshFromBmodel( entvars_t *pev, int modelindex ) +PxConvexMesh *CPhysicNovodex :: ConvexMeshFromBmodel( entvars_t *pev, int modelindex ) { if( !m_pCooking ) return NULL; // don't spam console about missed NxCooking.dll @@ -400,7 +281,7 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromBmodel( entvars_t *pev, int modeli } int numVerts = 0, totalVerts = 0; - NxConvexMesh *pHull = NULL; + PxConvexMesh *pHull = NULL; msurface_t *psurf; Vector *verts; int i, j; @@ -424,15 +305,14 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromBmodel( entvars_t *pev, int modeli } } - NxConvexMeshDesc meshDesc; - meshDesc.points = verts; - meshDesc.pointStrideBytes = sizeof(Vector); - meshDesc.numVertices = numVerts; - meshDesc.flags |= NX_CF_COMPUTE_CONVEX; - m_pCooking->NxInitCooking(); + PxConvexMeshDesc meshDesc; + meshDesc.points.data = verts; + meshDesc.points.stride = sizeof(Vector); + meshDesc.points.count = numVerts; + meshDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; MemoryWriteBuffer buf; - bool status = m_pCooking->NxCookConvexMesh( meshDesc, buf ); + bool status = m_pCooking->cookConvexMesh( meshDesc, buf ); delete [] verts; if( !status ) @@ -447,7 +327,7 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromBmodel( entvars_t *pev, int modeli return pHull; } -NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromBmodel( entvars_t *pev, int modelindex ) +PxTriangleMesh *CPhysicNovodex :: TriangleMeshFromBmodel( entvars_t *pev, int modelindex ) { if( !m_pCooking ) return NULL; // don't spam console about missed NxCooking.dll @@ -482,7 +362,7 @@ NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromBmodel( entvars_t *pev, int mo } int i, numElems = 0, totalElems = 0; - NxTriangleMesh *pMesh = NULL; + PxTriangleMesh *pMesh = NULL; msurface_t *psurf; // compute vertexes count @@ -493,7 +373,7 @@ NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromBmodel( entvars_t *pev, int mo } // create a temp indices array - NxU32 *indices = new NxU32[totalElems * 3]; + PxU32 *indices = new PxU32[totalElems * 3]; for( i = 0; i < bmodel->nummodelsurfaces; i++ ) { @@ -511,18 +391,23 @@ NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromBmodel( entvars_t *pev, int mo } } - NxTriangleMeshDesc meshDesc; - meshDesc.points = (const NxPoint*)&(bmodel->vertexes[0].position); // pointer to all vertices in the map - meshDesc.pointStrideBytes = sizeof( mvertex_t ); - meshDesc.triangleStrideBytes = 3 * sizeof( NxU32 ); - meshDesc.numVertices = bmodel->numvertexes; - meshDesc.numTriangles = numElems; - meshDesc.triangles = indices; - meshDesc.flags = 0; - m_pCooking->NxInitCooking(); + PxTriangleMeshDesc meshDesc; + meshDesc.points.count = bmodel->numvertexes; + meshDesc.points.stride = sizeof(mvertex_t); + meshDesc.points.data = (const PxVec3*)&(bmodel->vertexes[0].position); // pointer to all vertices in the map + meshDesc.triangles.count = numElems; + meshDesc.triangles.stride = 3 * sizeof(PxU32); + meshDesc.triangles.data = indices; + meshDesc.flags = (PxMeshFlags)0; + +#ifdef _DEBUG + // mesh should be validated before cooked without the mesh cleaning + bool res = m_pCooking->validateTriangleMesh(meshDesc); + PX_ASSERT(res); +#endif MemoryWriteBuffer buf; - bool status = m_pCooking->NxCookTriangleMesh( meshDesc, buf ); + bool status = m_pCooking->cookTriangleMesh( meshDesc, buf ); delete [] indices; if( !status ) @@ -532,7 +417,8 @@ NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromBmodel( entvars_t *pev, int mo } pMesh = m_pPhysics->createTriangleMesh( MemoryReadBuffer( buf.data )); - if( !pMesh ) ALERT( at_error, "failed to create triangle mesh from %s\n", bmodel->name ); + if( !pMesh ) + ALERT( at_error, "failed to create triangle mesh from %s\n", bmodel->name ); return pMesh; } @@ -575,7 +461,7 @@ void CPhysicNovodex :: StudioCalcBonePosition( mstudiobone_t *pbone, mstudioanim } } -NxConvexMesh *CPhysicNovodex :: ConvexMeshFromStudio( entvars_t *pev, int modelindex ) +PxConvexMesh *CPhysicNovodex :: ConvexMeshFromStudio( entvars_t *pev, int modelindex ) { if( UTIL_GetModelType( modelindex ) != mod_studio ) { @@ -593,7 +479,7 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromStudio( entvars_t *pev, int modeli } char szHullFilename[MAX_PATH]; - NxConvexMesh *pHull = NULL; + PxConvexMesh *pHull = NULL; HullNameForModel( smodel->name, szHullFilename, sizeof( szHullFilename )); @@ -672,7 +558,7 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromStudio( entvars_t *pev, int modeli Vector *m_verts = new Vector[psubmodel->numverts]; byte *pvertbone = ((byte *)phdr + psubmodel->vertinfoindex); Vector *verts = new Vector[psubmodel->numverts * 8]; // allocate temporary vertices array - NxU32 *indices = new NxU32[psubmodel->numverts * 24]; + PxU32 *indices = new PxU32[psubmodel->numverts * 24]; int numVerts = 0, numElems = 0; Vector tmp; @@ -737,17 +623,16 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromStudio( entvars_t *pev, int modeli } } - NxConvexMeshDesc meshDesc; - meshDesc.numTriangles = numElems / 3; - meshDesc.pointStrideBytes = sizeof(Vector); - meshDesc.triangleStrideBytes = 3 * sizeof( NxU32 ); - meshDesc.points = verts; - meshDesc.triangles = indices; - meshDesc.numVertices = numVerts; - meshDesc.flags |= NX_CF_COMPUTE_CONVEX; + PxConvexMeshDesc meshDesc; + meshDesc.indices.count = numElems / 3; + meshDesc.indices.data = indices; + meshDesc.indices.stride = 3 * sizeof( PxU32 ); + meshDesc.points.count = numVerts; + meshDesc.points.data = verts; + meshDesc.points.stride = sizeof(Vector); + meshDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; - m_pCooking->NxInitCooking(); - bool status = m_pCooking->NxCookConvexMesh( meshDesc, UserStream( szHullFilename, false )); + bool status = m_pCooking->cookConvexMesh( meshDesc, UserStream( szHullFilename, false )); delete [] verts; delete [] m_verts; @@ -760,12 +645,13 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromStudio( entvars_t *pev, int modeli } pHull = m_pPhysics->createConvexMesh( UserStream( szHullFilename, true )); - if( !pHull ) ALERT( at_error, "failed to create convex mesh from %s\n", smodel->name ); + if( !pHull ) + ALERT( at_error, "failed to create convex mesh from %s\n", smodel->name ); return pHull; } -NxTriangleMesh *CPhysicNovodex::TriangleMeshFromStudio(entvars_t *pev, int modelindex) +PxTriangleMesh *CPhysicNovodex::TriangleMeshFromStudio(entvars_t *pev, int modelindex) { if (UTIL_GetModelType(modelindex) != mod_studio) { @@ -801,7 +687,7 @@ NxTriangleMesh *CPhysicNovodex::TriangleMeshFromStudio(entvars_t *pev, int model } char szMeshFilename[MAX_PATH]; - NxTriangleMesh *pMesh = NULL; + PxTriangleMesh *pMesh = NULL; MeshNameForModel(smodel->name, szMeshFilename, sizeof(szMeshFilename)); @@ -887,7 +773,7 @@ NxTriangleMesh *CPhysicNovodex::TriangleMeshFromStudio(entvars_t *pev, int model } Vector *verts = new Vector[totalVertSize * 8]; // allocate temporary vertices array - NxU32 *indices = new NxU32[totalVertSize * 24]; + PxU32 *indices = new PxU32[totalVertSize * 24]; int numVerts = 0, numElems = 0; Vector tmp; @@ -977,17 +863,22 @@ NxTriangleMesh *CPhysicNovodex::TriangleMeshFromStudio(entvars_t *pev, int model delete[] m_verts; } - NxTriangleMeshDesc meshDesc; - meshDesc.numTriangles = numElems / 3; - meshDesc.pointStrideBytes = sizeof(Vector); - meshDesc.triangleStrideBytes = 3 * sizeof(NxU32); - meshDesc.points = verts; - meshDesc.triangles = indices; - meshDesc.numVertices = numVerts; - meshDesc.flags = 0; + PxTriangleMeshDesc meshDesc; + meshDesc.triangles.data = indices; + meshDesc.triangles.count = numElems / 3; + meshDesc.triangles.stride = 3 * sizeof(PxU32); + meshDesc.points.data = verts; + meshDesc.points.count = numVerts; + meshDesc.points.stride = sizeof(Vector); + meshDesc.flags = (PxMeshFlags)0; - m_pCooking->NxInitCooking(); - bool status = m_pCooking->NxCookTriangleMesh(meshDesc, UserStream(szMeshFilename, false)); +#ifdef _DEBUG + // mesh should be validated before cooked without the mesh cleaning + bool res = m_pCooking->validateTriangleMesh(meshDesc); + PX_ASSERT(res); +#endif + + bool status = m_pCooking->cookTriangleMesh(meshDesc, UserStream(szMeshFilename, false)); delete[] verts; delete[] indices; @@ -1004,7 +895,7 @@ NxTriangleMesh *CPhysicNovodex::TriangleMeshFromStudio(entvars_t *pev, int model return pMesh; } -NxConvexMesh *CPhysicNovodex :: ConvexMeshFromEntity( CBaseEntity *pObject ) +PxConvexMesh *CPhysicNovodex :: ConvexMeshFromEntity( CBaseEntity *pObject ) { if( !pObject || !m_pPhysics ) return NULL; @@ -1018,7 +909,7 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromEntity( CBaseEntity *pObject ) return NULL; } - NxConvexMesh *pCollision = NULL; + PxConvexMesh *pCollision = NULL; // call the apropriate loader switch( model->type ) @@ -1038,7 +929,7 @@ NxConvexMesh *CPhysicNovodex :: ConvexMeshFromEntity( CBaseEntity *pObject ) return pCollision; } -NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromEntity( CBaseEntity *pObject ) +PxTriangleMesh *CPhysicNovodex :: TriangleMeshFromEntity( CBaseEntity *pObject ) { if( !pObject || !m_pPhysics ) return NULL; @@ -1052,7 +943,7 @@ NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromEntity( CBaseEntity *pObject ) return NULL; } - NxTriangleMesh *pCollision = NULL; + PxTriangleMesh *pCollision = NULL; // call the apropriate loader switch( model->type ) @@ -1072,7 +963,7 @@ NxTriangleMesh *CPhysicNovodex :: TriangleMeshFromEntity( CBaseEntity *pObject ) return pCollision; } -NxActor *CPhysicNovodex :: ActorFromEntity( CBaseEntity *pObject ) +PxActor *CPhysicNovodex :: ActorFromEntity( CBaseEntity *pObject ) { if( FNullEnt( pObject ) || !pObject->m_pUserData ) return NULL; @@ -1083,10 +974,10 @@ NxActor *CPhysicNovodex :: ActorFromEntity( CBaseEntity *pObject ) return pVehicle->getActor(); } #endif - return (NxActor *)pObject->m_pUserData; + return (PxActor *)pObject->m_pUserData; } -CBaseEntity *CPhysicNovodex :: EntityFromActor( NxActor *pObject ) +CBaseEntity *CPhysicNovodex :: EntityFromActor( PxActor *pObject ) { if( !pObject || !pObject->userData ) return NULL; @@ -1094,24 +985,43 @@ CBaseEntity *CPhysicNovodex :: EntityFromActor( NxActor *pObject ) return CBaseEntity::Instance( (edict_t *)pObject->userData ); } -void *CPhysicNovodex :: CreateBodyFromEntity( CBaseEntity *pObject ) +bool CPhysicNovodex::CheckCollision(physx::PxRigidBody *pActor) { - NxConvexMesh *pCollision = ConvexMeshFromEntity( pObject ); - if( !pCollision ) return NULL; + std::vector shapes; + if (pActor->getNbShapes() > 0) + { + shapes.resize(pActor->getNbShapes()); + pActor->getShapes(&shapes[0], shapes.size()); + for (PxShape *shape : shapes) + { + if (shape->getFlags() & PxShapeFlag::eSIMULATION_SHAPE) { + return true; + } + } + } + return false; +} - NxBodyDesc BodyDesc; - NxActorDesc ActorDesc; - NxConvexShapeDesc meshShapeDesc; - BodyDesc.flags = NX_BF_VISUALIZATION|NX_BF_FILTER_SLEEP_VEL; - BodyDesc.solverIterationCount = SOLVER_ITERATION_COUNT; +void CPhysicNovodex::ToggleCollision(physx::PxRigidBody *pActor, bool enabled) +{ + std::vector shapes; + shapes.resize(pActor->getNbShapes()); + pActor->getShapes(&shapes[0], shapes.size()); - ActorDesc.body = &BodyDesc; - ActorDesc.density = DENSITY_FACTOR; - ActorDesc.userData = pObject->edict(); + for (PxShape *shape : shapes) { + shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, enabled); + } +} - meshShapeDesc.meshData = pCollision; - ActorDesc.shapes.pushBack( &meshShapeDesc ); - NxActor *pActor = m_pScene->createActor( ActorDesc ); +void *CPhysicNovodex :: CreateBodyFromEntity( CBaseEntity *pObject ) +{ + PxConvexMesh *pCollision = ConvexMeshFromEntity( pObject ); + if( !pCollision ) + return NULL; + + PxRigidDynamic *pActor = m_pPhysics->createRigidDynamic(PxTransform(PxIdentity)); + PxShape *pShape = PxRigidActorExt::createExclusiveShape(*pActor, PxConvexMeshGeometry(pCollision), *m_pDefaultMaterial); + //pShape->setContactOffset(2.0f); if( !pActor ) { @@ -1119,19 +1029,24 @@ void *CPhysicNovodex :: CreateBodyFromEntity( CBaseEntity *pObject ) return NULL; } - pActor->setName( pObject->GetClassname( )); - - NxMat34 pose; float mat[16]; + float maxSpeed = CVAR_GET_FLOAT( "sv_maxspeed" ); matrix4x4( pObject->GetAbsOrigin(), pObject->GetAbsAngles(), 1.0f ).CopyToArray( mat ); + PxTransform pose = PxTransform(PxMat44(mat)); - pose.setColumnMajor44( mat ); pActor->setGlobalPose( pose ); + pActor->setName( pObject->GetClassname() ); + //pActor->setSolverIterationCounts( SOLVER_ITERATION_COUNT ); + pActor->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD, true); pActor->setLinearVelocity( pObject->GetLocalVelocity() ); pActor->setAngularVelocity( pObject->GetLocalAvelocity() ); + pActor->setMaxLinearVelocity(maxSpeed); + pActor->setMaxAngularVelocity(720.0); + pActor->userData = pObject->edict(); + + m_pScene->addActor(*pActor); pObject->m_iActorType = ACTOR_DYNAMIC; pObject->m_pUserData = pActor; - return pActor; } @@ -1144,30 +1059,32 @@ used for characters: clients and monsters */ void *CPhysicNovodex :: CreateBoxFromEntity( CBaseEntity *pObject ) { - NxBodyDesc BodyDesc; - BodyDesc.flags |= NX_BF_KINEMATIC|NX_BF_VISUALIZATION; - - NxActorDesc ActorDesc; - NxBoxShapeDesc boxDesc; - boxDesc.dimensions = pObject->pev->size * PADDING_FACTOR; + PxBoxGeometry boxGeometry; + boxGeometry.halfExtents = PxVec3( + pObject->pev->size.x * PADDING_FACTOR / 2.f, + pObject->pev->size.y * PADDING_FACTOR / 2.f, + pObject->pev->size.z * PADDING_FACTOR / 2.f + ); - ActorDesc.body = &BodyDesc; - ActorDesc.density = DENSITY_FACTOR; - ActorDesc.userData = pObject->edict(); - ActorDesc.shapes.pushBack( &boxDesc ); + PxRigidDynamic *pActor = m_pPhysics->createRigidDynamic(PxTransform(PxIdentity)); + PxShape *pShape = PxRigidActorExt::createExclusiveShape(*pActor, boxGeometry, *m_pDefaultMaterial); - NxActor *pActor = m_pScene->createActor( ActorDesc ); - - if( !pActor ) + if (!pActor) { - ALERT( at_error, "failed to create rigidbody from entity %s\n", pObject->GetClassname( )); + ALERT( at_error, "failed to create rigidbody from entity %s\n", pObject->GetClassname()); return NULL; } - Vector vecOffset = (pObject->IsMonster()) ? Vector( 0, 0, pObject->pev->maxs.z / 2.0f ) : g_vecZero; + Vector origin = (pObject->IsMonster()) ? Vector( 0, 0, pObject->pev->maxs.z / 2.0f ) : g_vecZero; + origin += pObject->GetAbsOrigin(); + PxTransform pose = PxTransform(origin); + + pActor->setName(pObject->GetClassname()); + pActor->setGlobalPose(pose); + pActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + pActor->userData = pObject->edict(); + m_pScene->addActor(*pActor); - pActor->setName( pObject->GetClassname( )); - pActor->setGlobalPosition( pObject->GetAbsOrigin() + vecOffset ); pObject->m_iActorType = ACTOR_CHARACTER; pObject->m_pUserData = pActor; @@ -1176,27 +1093,15 @@ void *CPhysicNovodex :: CreateBoxFromEntity( CBaseEntity *pObject ) void *CPhysicNovodex :: CreateKinematicBodyFromEntity( CBaseEntity *pObject ) { - NxTriangleMesh *pCollision = TriangleMeshFromEntity( pObject ); - if( !pCollision ) return NULL; - - NxBodyDesc BodyDesc; - NxActorDesc ActorDesc; - NxTriangleMeshShapeDesc meshShapeDesc; - BodyDesc.flags = NX_BF_VISUALIZATION|NX_BF_KINEMATIC|NX_BF_FILTER_SLEEP_VEL; - BodyDesc.solverIterationCount = SOLVER_ITERATION_COUNT; - - if( !UTIL_CanRotate( pObject )) - BodyDesc.flags |= NX_BF_FROZEN_ROT; // entity missed origin-brush + PxTriangleMesh *pCollision = TriangleMeshFromEntity( pObject ); + if (!pCollision) + return NULL; - ActorDesc.body = &BodyDesc; - ActorDesc.density = DENSITY_FACTOR; - ActorDesc.userData = pObject->edict(); + //if( !UTIL_CanRotate( pObject )) + // BodyDesc.flags |= NX_BF_FROZEN_ROT; // entity missed origin-brush - meshShapeDesc.meshData = pCollision; - ActorDesc.shapes.pushBack( &meshShapeDesc ); - m_ErrorStream.hideWarning( true ); - NxActor *pActor = m_pScene->createActor( ActorDesc ); - m_ErrorStream.hideWarning( false ); + PxRigidDynamic *pActor = m_pPhysics->createRigidDynamic(PxTransform(PxIdentity)); + PxShape *pShape = PxRigidActorExt::createExclusiveShape(*pActor, PxTriangleMeshGeometry(pCollision), *m_pDefaultMaterial); if( !pActor ) { @@ -1204,42 +1109,37 @@ void *CPhysicNovodex :: CreateKinematicBodyFromEntity( CBaseEntity *pObject ) return NULL; } - pActor->setName( pObject->GetClassname( )); - - NxMat34 pose; float mat[16]; matrix4x4( pObject->GetAbsOrigin(), pObject->GetAbsAngles(), 1.0f ).CopyToArray( mat ); - pose.setColumnMajor44( mat ); + PxTransform pose = PxTransform(PxMat44(mat)); + pActor->setName(pObject->GetClassname()); pActor->setGlobalPose( pose ); + //pActor->setSolverIterationCounts(SOLVER_ITERATION_COUNT); + pActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + pActor->userData = pObject->edict(); + //m_ErrorCallback.hideWarning(true); + m_pScene->addActor(*pActor); + //m_ErrorCallback.hideWarning(false); pObject->m_iActorType = ACTOR_KINEMATIC; pObject->m_pUserData = pActor; - + return pActor; } void *CPhysicNovodex :: CreateStaticBodyFromEntity( CBaseEntity *pObject ) { - NxTriangleMesh *pCollision = TriangleMeshFromEntity( pObject ); - if( !pCollision ) return NULL; + PxTriangleMesh *pCollision = TriangleMeshFromEntity( pObject ); + if( !pCollision ) + return NULL; - NxMat34 pose; float mat[16]; - matrix4x4( pObject->GetAbsOrigin(), pObject->GetAbsAngles(), 1.0f ).CopyToArray( mat ); + matrix4x4(pObject->GetAbsOrigin(), pObject->GetAbsAngles(), 1.0f).CopyToArray(mat); - pose.setColumnMajor44( mat ); - - NxActorDesc ActorDesc; - NxTriangleMeshShapeDesc meshShapeDesc; - ActorDesc.density = DENSITY_FACTOR; - ActorDesc.userData = pObject->edict(); - ActorDesc.globalPose = pose; - - if( pObject->pev->flags & FL_CONVEYOR ) - meshShapeDesc.materialIndex = 1; - meshShapeDesc.meshData = pCollision; - ActorDesc.shapes.pushBack( &meshShapeDesc ); - NxActor *pActor = m_pScene->createActor( ActorDesc ); + PxTransform pose = PxTransform(PxMat44(mat)); + PxMaterial *material = (pObject->pev->flags & FL_CONVEYOR) ? m_pConveyorMaterial : m_pDefaultMaterial; + PxRigidStatic *pActor = m_pPhysics->createRigidStatic(pose); + PxShape *pShape = PxRigidActorExt::createExclusiveShape(*pActor, PxTriangleMeshGeometry(pCollision), *material); if( !pActor ) { @@ -1248,7 +1148,9 @@ void *CPhysicNovodex :: CreateStaticBodyFromEntity( CBaseEntity *pObject ) } pActor->setName( pObject->GetClassname( )); -// pActor->setGlobalPose( pose ); + pActor->userData = pObject->edict(); + m_pScene->addActor(*pActor); + pObject->m_iActorType = ACTOR_STATIC; pObject->m_pUserData = pActor; @@ -1275,7 +1177,7 @@ void *CPhysicNovodex :: CreateVehicle( CBaseEntity *pObject, string_t scriptName { ALERT( at_error, "CreateVehicle: couldn't load %s\n", STRING( scriptName )); return NULL; - } + } model_t *smodel = (model_t *)MODEL_HANDLE( pObject->pev->modelindex ); studiohdr_t *phdr = (studiohdr_t *)smodel->cache.data; @@ -1484,25 +1386,25 @@ void CPhysicNovodex :: UpdateVehicle( CBaseEntity *pObject ) #endif } -bool CPhysicNovodex :: UpdateEntityPos( CBaseEntity *pEntity ) +bool CPhysicNovodex::UpdateEntityPos( CBaseEntity *pEntity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - - if( !pActor || pActor->isSleeping( )) + PxActor *pActor = ActorFromEntity(pEntity); + if (!pActor) return false; - NxMat34 pose = pActor->getGlobalPose(); - float mat[16]; + PxRigidDynamic *pDynamicActor = pActor->is(); + if (!pDynamicActor || pDynamicActor->isSleeping()) + return false; - pose.getColumnMajor44( mat ); - matrix4x4 m( mat ); + PxTransform pose = pDynamicActor->getGlobalPose(); + matrix4x4 m( PxMat44(pose).front() ); Vector angles = m.GetAngles(); Vector origin = m.GetOrigin(); // store actor velocities too - pEntity->SetLocalVelocity( pActor->getLinearVelocity() ); - pEntity->SetLocalAvelocity( pActor->getAngularVelocity() ); + pEntity->SetLocalVelocity( pDynamicActor->getLinearVelocity() ); + pEntity->SetLocalAvelocity( pDynamicActor->getAngularVelocity() ); Vector vecPrevOrigin = pEntity->GetAbsOrigin(); pEntity->SetLocalAngles( angles ); @@ -1514,22 +1416,22 @@ bool CPhysicNovodex :: UpdateEntityPos( CBaseEntity *pEntity ) bool CPhysicNovodex :: UpdateActorPos( CBaseEntity *pEntity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return false; + PxRigidActor *pActor = ActorFromEntity( pEntity )->is(); + if( !pActor ) + return false; - NxMat34 pose; float mat[16]; - - matrix4x4 m( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), 1.0f ); + matrix4x4 m( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), 1.0f ); m.CopyToArray( mat ); - pose.setColumnMajor44( mat ); + PxTransform pose = PxTransform(PxMat44(mat)); pActor->setGlobalPose( pose ); - if( !pActor->readBodyFlag( NX_BF_KINEMATIC )) + PxRigidDynamic *pRigidDynamic = ActorFromEntity(pEntity)->is(); + if (!(pRigidDynamic->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) { - pActor->setLinearVelocity( pEntity->GetLocalVelocity() ); - pActor->setAngularVelocity( pEntity->GetLocalAvelocity() ); + pRigidDynamic->setLinearVelocity( pEntity->GetLocalVelocity() ); + pRigidDynamic->setAngularVelocity( pEntity->GetLocalAvelocity() ); } return true; @@ -1537,30 +1439,37 @@ bool CPhysicNovodex :: UpdateActorPos( CBaseEntity *pEntity ) bool CPhysicNovodex :: IsBodySleeping( CBaseEntity *pEntity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return false; - return pActor->isSleeping(); + PxActor *pActor = ActorFromEntity(pEntity); + if (!pActor) + return false; + + PxRigidDynamic *pDynamicActor = ActorFromEntity(pEntity)->is(); + if (!pDynamicActor) + return false; + + return pDynamicActor->isSleeping(); } void CPhysicNovodex :: UpdateEntityAABB( CBaseEntity *pEntity ) { - NxActor *pActor = ActorFromEntity( pEntity ); + PxU32 boundsCount; + PxActor *pActor = ActorFromEntity( pEntity ); + if (!pActor) + return; - if( !pActor || pActor->getNbShapes() <= 0 ) + PxRigidActor *pRigidActor = pActor->is(); + if (!pRigidActor || pRigidActor->getNbShapes() <= 0) return; ClearBounds( pEntity->pev->absmin, pEntity->pev->absmax ); - - for( uint i = 0; i < pActor->getNbShapes(); i++ ) + PxTransform globalPose = pRigidActor->getGlobalPose(); + PxBounds3 *boundsList = PxRigidActorExt::getRigidActorShapeLocalBoundsList(*pRigidActor, boundsCount); + + for (PxU32 i = 0; i < boundsCount; i++) { - NxShape *pShape = pActor->getShapes()[i]; - NxBounds3 bbox; - - // already transformed as OBB - pShape->getWorldBounds( bbox ); - - AddPointToBounds( bbox.min, pEntity->pev->absmin, pEntity->pev->absmax ); - AddPointToBounds( bbox.max, pEntity->pev->absmin, pEntity->pev->absmax ); + const PxBounds3 &bbox = boundsList[i]; // already transformed as OBB + AddPointToBounds( globalPose.transform(bbox.minimum), pEntity->pev->absmin, pEntity->pev->absmax ); + AddPointToBounds( globalPose.transform(bbox.maximum), pEntity->pev->absmin, pEntity->pev->absmax ); } // shrink AABB by 1 units in each axis @@ -1629,7 +1538,7 @@ collect all the info from generic actor */ void CPhysicNovodex :: SaveBody( CBaseEntity *pEntity ) { - NxActor *pActor = ActorFromEntity( pEntity ); + PxRigidDynamic *pActor = ActorFromEntity( pEntity )->is(); if( !pActor ) { @@ -1637,16 +1546,20 @@ void CPhysicNovodex :: SaveBody( CBaseEntity *pEntity ) return; } - NxActorDesc actorDesc; - NxBodyDesc bodyDesc; + PxShape *shape; + pActor->getShapes(&shape, 1); + PxFilterData filterData = shape->getSimulationFilterData(); - pActor->saveToDesc( actorDesc ); - pActor->saveBodyToDesc( bodyDesc ); + // filter data get and stored only for one shape, but it can be more than one + // assumed that all attached shapes have same filter data + pEntity->m_iFilterData[0] = filterData.word0; + pEntity->m_iFilterData[1] = filterData.word1; + pEntity->m_iFilterData[2] = filterData.word2; + pEntity->m_iFilterData[3] = filterData.word3; - pEntity->m_iActorFlags = actorDesc.flags; - pEntity->m_iBodyFlags = bodyDesc.flags; - pEntity->m_usActorGroup = actorDesc.group; - pEntity->m_flBodyMass = bodyDesc.mass; + pEntity->m_iActorFlags = pActor->getActorFlags(); + pEntity->m_iBodyFlags = pActor->getRigidBodyFlags(); + pEntity->m_flBodyMass = pActor->getMass(); pEntity->m_fFreezed = pActor->isSleeping(); if( pEntity->m_iActorType == ACTOR_DYNAMIC ) @@ -1666,208 +1579,221 @@ re-create shape, apply physic params void *CPhysicNovodex :: RestoreBody( CBaseEntity *pEntity ) { // physics not initialized? - if( !m_pScene ) return NULL; + if (!m_pScene) + return NULL; + + PxShape *pShape; + PxRigidDynamic *pActor; + PxTransform pose; + Vector angles = pEntity->GetAbsAngles(); - NxConvexShapeDesc meshShapeDesc; - NxTriangleMeshShapeDesc triMeshShapeDesc; - NxBoxShapeDesc boxDesc; - NxActorDesc ActorDesc; - NxBodyDesc BodyDesc; + if (pEntity->m_iActorType == ACTOR_CHARACTER) { + angles = g_vecZero; // no angles for NPC and client + } - switch( pEntity->m_iActorType ) + float mat[16]; + matrix4x4 m(pEntity->GetAbsOrigin(), angles, 1.0f); + m.CopyToArray(mat); + + pose = PxTransform(PxMat44(mat)); + pActor = m_pPhysics->createRigidDynamic(pose); + + if (!pActor) { - case ACTOR_DYNAMIC: - meshShapeDesc.meshData = ConvexMeshFromEntity( pEntity ); - if( !meshShapeDesc.meshData ) return NULL; - ActorDesc.shapes.pushBack( &meshShapeDesc ); - break; - case ACTOR_CHARACTER: - boxDesc.dimensions = pEntity->pev->size * PADDING_FACTOR; - ActorDesc.shapes.pushBack( &boxDesc ); - break; - case ACTOR_KINEMATIC: - case ACTOR_STATIC: - triMeshShapeDesc.meshData = TriangleMeshFromEntity( pEntity ); - if( !triMeshShapeDesc.meshData ) return NULL; - if( pEntity->pev->flags & FL_CONVEYOR ) - triMeshShapeDesc.materialIndex = 1; - ActorDesc.shapes.pushBack( &triMeshShapeDesc ); - break; - default: - ALERT( at_error, "RestoreBody: invalid actor type %i\n", pEntity->m_iActorType ); + ALERT(at_error, "RestoreBody: unbale to create actor with type (%i)\n", pEntity->m_iActorType); return NULL; } - if( ActorDesc.shapes.size() <= 0 ) - return NULL; // failed to create shape - - NxMat34 pose; - float mat[16]; + switch( pEntity->m_iActorType ) + { + case ACTOR_DYNAMIC: + { + PxConvexMesh *convexMesh = ConvexMeshFromEntity(pEntity); + if (!convexMesh) + return NULL; - Vector angles = pEntity->GetAbsAngles(); + pShape = PxRigidActorExt::createExclusiveShape(*pActor, PxConvexMeshGeometry(convexMesh), *m_pDefaultMaterial); + break; + } + case ACTOR_CHARACTER: + { + PxBoxGeometry box; + box.halfExtents = pEntity->pev->size * PADDING_FACTOR; + pShape = PxRigidActorExt::createExclusiveShape(*pActor, box, *m_pDefaultMaterial); + break; + } + case ACTOR_KINEMATIC: + case ACTOR_STATIC: + { + PxTriangleMesh *triangleMesh = TriangleMeshFromEntity(pEntity); + if (!triangleMesh) + return NULL; - if( pEntity->m_iActorType == ACTOR_CHARACTER ) - angles = g_vecZero; // no angles for NPC and client + PxMaterial *pMaterial = m_pDefaultMaterial; + if (pEntity->pev->flags & FL_CONVEYOR) { + pMaterial = m_pConveyorMaterial; + } + pShape = PxRigidActorExt::createExclusiveShape(*pActor, PxTriangleMeshGeometry(triangleMesh), *pMaterial); + break; + } + default: + { + ALERT(at_error, "RestoreBody: invalid actor type %i\n", pEntity->m_iActorType); + return NULL; + } + } - matrix4x4 m( pEntity->GetAbsOrigin(), angles, 1.0f ); - m.CopyToArray( mat ); - pose.setColumnMajor44( mat ); + if (!pShape) + return NULL; // failed to create shape // fill in actor description - if( pEntity->m_iActorType != ACTOR_STATIC ) + if (pEntity->m_iActorType != ACTOR_STATIC) { - BodyDesc.flags = pEntity->m_iBodyFlags; -// BodyDesc.mass = pEntity->m_flBodyMass; - BodyDesc.solverIterationCount = SOLVER_ITERATION_COUNT; + pActor->setRigidBodyFlags(static_cast(pEntity->m_iBodyFlags)); + pActor->setMass(pEntity->m_flBodyMass); + pActor->setSolverIterationCounts(SOLVER_ITERATION_COUNT); - if( pEntity->m_iActorType != ACTOR_KINEMATIC ) + if (pEntity->m_iActorType != ACTOR_KINEMATIC) { - BodyDesc.linearVelocity = pEntity->GetAbsVelocity(); - BodyDesc.angularVelocity = pEntity->GetAbsAvelocity(); + pActor->setLinearVelocity(pEntity->GetAbsVelocity()); + pActor->setAngularVelocity(pEntity->GetAbsAvelocity()); } - - ActorDesc.body = &BodyDesc; - } + } - ActorDesc.density = DENSITY_FACTOR; - ActorDesc.userData = pEntity->edict(); - ActorDesc.flags = pEntity->m_iActorFlags; - ActorDesc.group = pEntity->m_usActorGroup; - ActorDesc.globalPose = pose; // saved pose + //ActorDesc.density = DENSITY_FACTOR; + pActor->userData = pEntity->edict(); + pActor->setActorFlags(static_cast(pEntity->m_iActorFlags)); - if( pEntity->m_iActorType == ACTOR_KINEMATIC ) - m_ErrorStream.hideWarning( true ); + //if( pEntity->m_iActorType == ACTOR_KINEMATIC ) + // m_ErrorCallback.hideWarning( true ); - NxActor *pActor = m_pScene->createActor( ActorDesc ); - - if( pEntity->m_iActorType == ACTOR_KINEMATIC ) - m_ErrorStream.hideWarning( false ); - - if( !pActor ) - { - ALERT( at_error, "RestoreBody: unbale to create actor with type (%i)\n", pEntity->m_iActorType ); - return NULL; - } + //if( pEntity->m_iActorType == ACTOR_KINEMATIC ) + // m_ErrorCallback.hideWarning( false ); // apply specific actor params - pActor->setName( pEntity->GetClassname( )); + pActor->setName( pEntity->GetClassname() ); pEntity->m_pUserData = pActor; - if( pEntity->m_fFreezed && pEntity->m_iActorType == ACTOR_DYNAMIC ) + if (pEntity->m_fFreezed && pEntity->m_iActorType == ACTOR_DYNAMIC) { pActor->putToSleep(); + } + m_pScene->addActor(*pActor); return pActor; } void CPhysicNovodex :: SetAngles( CBaseEntity *pEntity, const Vector &angles ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; + + float mat[9]; + matrix3x3 m(angles); + m.CopyToArray(mat); - NxMat33 rot; - matrix3x3 m( angles ); - rot.setRowMajor( (float *)m ); - pActor->setGlobalOrientation( rot ); + PxRigidDynamic *pRigidDynamic = pActor->is(); + PxTransform transform = pRigidDynamic->getGlobalPose(); + transform.q = PxQuat(PxMat33( mat )); + pRigidDynamic->setGlobalPose(transform); } void CPhysicNovodex :: SetOrigin( CBaseEntity *pEntity, const Vector &origin ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; - pActor->setGlobalPosition( origin ); + PxRigidDynamic *pRigidDynamic = pActor->is(); + PxTransform transform = pRigidDynamic->getGlobalPose(); + transform.p = origin; + pRigidDynamic->setGlobalPose(transform); } void CPhysicNovodex :: SetVelocity( CBaseEntity *pEntity, const Vector &velocity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; - pActor->setLinearVelocity( velocity ); + PxRigidDynamic *pRigidDynamic = pActor->is(); + pRigidDynamic->setLinearVelocity( velocity ); } void CPhysicNovodex :: SetAvelocity( CBaseEntity *pEntity, const Vector &velocity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; - pActor->setAngularVelocity( velocity ); + PxRigidDynamic *pRigidDynamic = pActor->is(); + pRigidDynamic->setAngularVelocity( velocity ); } void CPhysicNovodex :: MoveObject( CBaseEntity *pEntity, const Vector &finalPos ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; - pActor->moveGlobalPosition( finalPos ); + PxRigidDynamic *pRigidDynamic = pActor->is(); + PxTransform pose = pRigidDynamic->getGlobalPose(); + pose.p = finalPos; + pRigidDynamic->setKinematicTarget(pose); } void CPhysicNovodex :: RotateObject( CBaseEntity *pEntity, const Vector &finalAngle ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; - NxMat33 rot; - matrix3x3 m( finalAngle ); - rot.setRowMajor( (float *)m ); - pActor->moveGlobalOrientation( rot ); + PxRigidDynamic *pRigidDynamic = pActor->is(); + PxTransform pose = pRigidDynamic->getGlobalPose(); + matrix3x3 m(finalAngle); + pose.q = PxQuat(PxMat33(m)); + pRigidDynamic->setKinematicTarget(pose); } void CPhysicNovodex :: SetLinearMomentum( CBaseEntity *pEntity, const Vector &velocity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; - pActor->setLinearMomentum( velocity ); + // TODO modern PhysX SDK doesn't have method setLinearMomentum for dynamic bodies + // so maybe we just can replace it with setLinearVelocity(velocity * mass)? + // i'm not sure in this since so weak at physics theory and terminology + PxRigidDynamic *pRigidDynamic = pActor->is(); + pRigidDynamic->setLinearVelocity( velocity * pRigidDynamic->getMass() ); } void CPhysicNovodex :: AddImpulse( CBaseEntity *pEntity, const Vector &impulse, const Vector &position, float factor ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; + PxActor *pActor = ActorFromEntity( pEntity ); + if (!pActor) + return; - NxF32 coeff = (1000.0f / pActor->getMass()) * factor; + PxRigidBody *pRigidBody = pActor->is(); + PxF32 coeff = (1000.0f / pRigidBody->getMass()) * factor; // prevent to apply too much impulse - if( pActor->getMass() < 8.0f ) + if (pRigidBody->getMass() < 8.0f) { coeff *= 0.0001f; } - pActor->addForceAtPos( NxVec3(impulse * coeff), (NxVec3)position, NX_IMPULSE ); + PxRigidBodyExt::addForceAtPos(*pRigidBody, PxVec3(impulse * coeff), position, PxForceMode::eIMPULSE); } void CPhysicNovodex :: AddForce( CBaseEntity *pEntity, const Vector &force ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor ) return; - - pActor->addForce( force, NX_FORCE ); -} + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; -void *CPhysicNovodex :: CreateForceField( CBaseEntity *pEntity, const Vector &force ) -{ - // create a forcefield kernel - NxForceFieldLinearKernelDesc linearKernelDesc; - - linearKernelDesc.constant = NxVec3( 1, 0, 0.2 );//force; -// linearKernelDesc.velocityTarget = NxVec3( 0.5, 0, 0.2 ); -// linearKernelDesc.falloffLinear = NxVec3( 100, 0, 0.2 ); - NxForceFieldLinearKernel *pLinearKernel; - pLinearKernel = m_pScene->createForceFieldLinearKernel( linearKernelDesc ); - - // The forcefield descriptor - NxForceFieldDesc fieldDesc; - fieldDesc.kernel = pLinearKernel; - fieldDesc.rigidBodyType = NX_FF_TYPE_OTHER; - - // A box forcefield shape descriptor - NxBoxForceFieldShapeDesc box; - box.dimensions = (pEntity->pev->size - Vector( 2, 2, 2 )) * 0.5f; - box.pose.t = pEntity->Center() + Vector( 0, 0, pEntity->pev->size.z - 2 ); - - fieldDesc.includeGroupShapes.push_back( &box ); - return m_pScene->createForceField( fieldDesc ); + PxRigidBody *pRigidBody = pActor->is(); + pRigidBody->addForce(force); } int CPhysicNovodex :: FLoadTree( char *szMapName ) @@ -1883,12 +1809,12 @@ int CPhysicNovodex :: FLoadTree( char *szMapName ) return 1; } - if( m_pSceneActor ) - m_pScene->releaseActor( *m_pSceneActor ); + if (m_pSceneActor) + m_pSceneActor->release(); m_pSceneActor = NULL; - if( m_pSceneMesh ) - m_pPhysics->releaseTriangleMesh( *m_pSceneMesh ); + if (m_pSceneMesh) + m_pSceneMesh->release(); m_pSceneMesh = NULL; m_fLoaded = FALSE; // trying to load new collision tree @@ -2024,12 +1950,12 @@ int CPhysicNovodex :: BuildCollisionTree( char *szMapName ) return FALSE; } - if( m_pSceneActor ) - m_pScene->releaseActor( *m_pSceneActor ); + if (m_pSceneActor) + m_pSceneActor->release(); m_pSceneActor = NULL; - if( m_pSceneMesh ) - m_pPhysics->releaseTriangleMesh( *m_pSceneMesh ); + if (m_pSceneMesh) + m_pSceneMesh->release(); m_pSceneMesh = NULL; // save off mapname @@ -2052,7 +1978,7 @@ int CPhysicNovodex :: BuildCollisionTree( char *szMapName ) totalElems += (psurf->numedges - 2); } - NxU32 *indices = new NxU32[totalElems * 3]; + PxU32 *indices = new PxU32[totalElems * 3]; for( i = 0; i < m_pWorldModel->nummodelsurfaces; i++ ) { @@ -2072,26 +1998,25 @@ int CPhysicNovodex :: BuildCollisionTree( char *szMapName ) } } - NX_ASSERT( totalElems == numElems ); + PX_ASSERT( totalElems == numElems ); // build physical model - NxTriangleMeshDesc levelDesc; - levelDesc.pointStrideBytes = sizeof( mvertex_t ); - levelDesc.triangleStrideBytes = 3 * sizeof( NxU32 ); - levelDesc.points = (const NxPoint*)&(m_pWorldModel->vertexes[0].position); - levelDesc.numVertices = m_pWorldModel->numvertexes; - levelDesc.numTriangles = numElems; - levelDesc.triangles = indices; - levelDesc.flags = 0; + PxTriangleMeshDesc levelDesc; + levelDesc.points.count = m_pWorldModel->numvertexes; + levelDesc.points.data = (const PxVec3*)&(m_pWorldModel->vertexes[0].position); + levelDesc.points.stride = sizeof(mvertex_t); + levelDesc.triangles.count = numElems; + levelDesc.triangles.data = indices; + levelDesc.triangles.stride = 3 * sizeof(PxU32); + levelDesc.flags = (PxMeshFlags)0; char szHullFilename[MAX_PATH]; Q_snprintf( szHullFilename, sizeof( szHullFilename ), "cache/maps/%s.bin", szMapName ); if( m_pCooking ) { - m_pCooking->NxInitCooking(); - bool status = m_pCooking->NxCookTriangleMesh( levelDesc, UserStream( szHullFilename, false )); - } + bool status = m_pCooking->cookTriangleMesh( levelDesc, UserStream( szHullFilename, false )); + } delete [] indices; @@ -2101,36 +2026,36 @@ int CPhysicNovodex :: BuildCollisionTree( char *szMapName ) return (m_pSceneMesh != NULL) ? TRUE : FALSE; } -void CPhysicNovodex :: SetupWorld( void ) +void CPhysicNovodex::SetupWorld(void) { - if( m_pSceneActor ) + if (m_pSceneActor) return; // already loaded - if( !m_pSceneMesh ) + if (!m_pSceneMesh) { - ALERT( at_error, "*collision tree not ready!\n" ); + ALERT(at_error, "*collision tree not ready!\n"); return; } // get a world struct - if(( m_pWorldModel = (model_t *)MODEL_HANDLE( 1 )) == NULL ) + if ((m_pWorldModel = (model_t *)MODEL_HANDLE(1)) == NULL) { - ALERT( at_error, "SetupWorld: unbale to fetch world pointer %s\n", m_szMapName ); + ALERT(at_error, "SetupWorld: unbale to fetch world pointer %s\n", m_szMapName); return; } - NxTriangleMeshShapeDesc levelShapeDesc; - NxActorDesc ActorDesc; + PxRigidStatic *pActor = m_pPhysics->createRigidStatic(PxTransform(PxVec3(PxZero), PxQuat(PxIdentity))); + PxShape *pShape = PxRigidActorExt::createExclusiveShape(*pActor, PxTriangleMeshGeometry(m_pSceneMesh), *m_pDefaultMaterial); - ActorDesc.userData = g_pWorld->edict(); - levelShapeDesc.meshData = m_pSceneMesh; - ActorDesc.shapes.pushBack( &levelShapeDesc ); - m_pSceneActor = m_pScene->createActor( ActorDesc ); + pActor->setName(g_pWorld->GetClassname()); + pActor->userData = g_pWorld->edict(); + m_pScene->addActor(*pActor); + m_pSceneActor = pActor; m_fLoaded = true; - // update the world bounds for NX_BP_TYPE_SAP_MULTI - NxShape *pSceneShape = m_pSceneActor->getShapes()[0]; - pSceneShape->getWorldBounds( worldBounds ); + PxU32 boundsCount; + PxBounds3 *boundsList = PxRigidActorExt::getRigidActorShapeLocalBoundsList(*pActor, boundsCount); + m_worldBounds = boundsList[0]; } void CPhysicNovodex :: DebugDraw( void ) @@ -2138,7 +2063,7 @@ void CPhysicNovodex :: DebugDraw( void ) if( !m_pPhysics || !m_pScene ) return; - gDebugRenderer.renderData( *m_pScene->getDebugRenderable( )); + DebugRenderer::GetInstance().RenderData(m_pScene->getRenderBuffer()); } /* @@ -2195,50 +2120,66 @@ void CPhysicNovodex :: DrawPSpeeds( void ) } } -void CPhysicNovodex :: FreeAllBodies( void ) +void CPhysicNovodex :: FreeAllBodies() { - if( !m_pScene ) return; + if( !m_pScene ) + return; + + PxActorTypeFlags actorFlags = ( + PxActorTypeFlag::eRIGID_STATIC | + PxActorTypeFlag::eRIGID_DYNAMIC + ); + + std::vector actors; + actors.assign(m_pScene->getNbActors(actorFlags), nullptr); + m_pScene->getActors(actorFlags, actors.data(), actors.size() * sizeof(actors[0])); // throw all bodies - while( m_pScene->getNbActors()) + for (PxActor *actor : actors) { - NxActor *actor = m_pScene->getActors()[0]; - m_pScene->releaseActor( *actor ); + m_pScene->removeActor(*actor); + actor->release(); } m_pSceneActor = NULL; } void CPhysicNovodex :: TeleportCharacter( CBaseEntity *pEntity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor || pActor->getNbShapes() <= 0 ) + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) return; - if( m_fNeedFetchResults ) + PxRigidBody *pRigidBody = pActor->is(); + if(pRigidBody->getNbShapes() <= 0 || m_fNeedFetchResults ) return; - NxBoxShape *pShape = (NxBoxShape *)pActor->getShapes()[0]; + PxShape *pShape; + pRigidBody->getShapes(&pShape, sizeof(pShape)); // get only first shape, but it can be several Vector vecOffset = (pEntity->IsMonster()) ? Vector( 0, 0, pEntity->pev->maxs.z / 2.0f ) : g_vecZero; - pShape->setDimensions( pEntity->pev->size * PADDING_FACTOR ); - pActor->setGlobalPosition( pEntity->GetAbsOrigin() + vecOffset ); + if (pShape->getGeometryType() == PxGeometryType::eBOX) + { + PxBoxGeometry &box = pShape->getGeometry().box(); + PxTransform pose = pRigidBody->getGlobalPose(); + box.halfExtents = PxVec3(pEntity->pev->size * PADDING_FACTOR); + pose.p = (pEntity->GetAbsOrigin() + vecOffset); + pRigidBody->setGlobalPose(pose); + } + else { + ALERT(at_error, "TeleportCharacter: shape geometry type is not a box\n"); + } } void CPhysicNovodex :: TeleportActor( CBaseEntity *pEntity ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor || pActor->getNbShapes() <= 0 ) + PxActor *pActor = ActorFromEntity( pEntity ); + if (!pActor) return; - NxMat34 pose; - float mat[16]; - - matrix4x4 m( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles( ), 1.0f ); - m.CopyToArray( mat ); - - // complex move for kinematic entities - pose.setColumnMajor44( mat ); - pActor->setGlobalPose( pose ); + PxRigidBody *pRigidBody = pActor->is(); + matrix4x4 m(pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), 1.0f); + PxTransform pose = PxTransform(PxMat44(m)); + pRigidBody->setGlobalPose( pose ); } void CPhysicNovodex :: MoveCharacter( CBaseEntity *pEntity ) @@ -2246,25 +2187,34 @@ void CPhysicNovodex :: MoveCharacter( CBaseEntity *pEntity ) if( !pEntity || pEntity->m_vecOldPosition == pEntity->pev->origin ) return; - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor || pActor->getNbShapes() <= 0 ) + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) return; - if( m_fNeedFetchResults ) + PxRigidDynamic *pRigidBody = pActor->is(); + if( m_fNeedFetchResults || pRigidBody->getNbShapes() <= 0) return; - NxBoxShape *pShape = (NxBoxShape *)pActor->getShapes()[0]; - - // if were in NOCLIP or FLY (ladder climbing) mode - disable collisions - if( pEntity->pev->movetype != MOVETYPE_WALK ) - pActor->raiseActorFlag( NX_AF_DISABLE_COLLISION ); - else pActor->clearActorFlag( NX_AF_DISABLE_COLLISION ); + PxShape *pShape; + pRigidBody->getShapes(&pShape, sizeof(pShape)); // get only first shape, but it can be several + if (pShape->getGeometryType() == PxGeometryType::eBOX) + { + PxBoxGeometry &box = pShape->getGeometry().box(); - Vector vecOffset = (pEntity->IsMonster()) ? Vector( 0, 0, pEntity->pev->maxs.z / 2.0f ) : g_vecZero; + // if were in NOCLIP or FLY (ladder climbing) mode - disable collisions + if (pEntity->pev->movetype != MOVETYPE_WALK) + ToggleCollision(pRigidBody, false); + else + ToggleCollision(pRigidBody, true); - pShape->setDimensions( pEntity->pev->size * PADDING_FACTOR ); - pActor->moveGlobalPosition( pEntity->GetAbsOrigin() + vecOffset ); - pEntity->m_vecOldPosition = pEntity->GetAbsOrigin(); // update old position + Vector vecOffset = (pEntity->IsMonster()) ? Vector( 0, 0, pEntity->pev->maxs.z / 2.0f ) : g_vecZero; + box.halfExtents = ( pEntity->pev->size * PADDING_FACTOR ); + pShape->setGeometry(box); // should we do this? + PxTransform pose = pRigidBody->getGlobalPose(); + pose.p = (pEntity->GetAbsOrigin() + vecOffset); + pRigidBody->setKinematicTarget(pose); + pEntity->m_vecOldPosition = pEntity->GetAbsOrigin(); // update old position + } } void CPhysicNovodex :: MoveKinematic( CBaseEntity *pEntity ) @@ -2272,63 +2222,77 @@ void CPhysicNovodex :: MoveKinematic( CBaseEntity *pEntity ) if( !pEntity || ( pEntity->pev->movetype != MOVETYPE_PUSH && pEntity->pev->movetype != MOVETYPE_PUSHSTEP )) return; // probably not a mover - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor || pActor->getNbShapes() <= 0 ) + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) return; - if( m_fNeedFetchResults ) + PxRigidDynamic *pRigidBody = pActor->is(); + if( m_fNeedFetchResults || pRigidBody->getNbShapes() <= 0) return; if( pEntity->pev->solid == SOLID_NOT || pEntity->pev->solid == SOLID_TRIGGER ) - pActor->raiseActorFlag( NX_AF_DISABLE_COLLISION ); - else pActor->clearActorFlag( NX_AF_DISABLE_COLLISION ); - - NxMat34 pose; - float mat[16]; + ToggleCollision(pRigidBody, false); + else + ToggleCollision(pRigidBody, true); - matrix4x4 m( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles( ), 1.0f ); - m.CopyToArray( mat ); + PxTransform pose; + matrix4x4 m( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles( ), 1.0f ); // complex move for kinematic entities - pose.setColumnMajor44( mat ); - pActor->moveGlobalPose( pose ); + pose = PxTransform(PxMat44(m)); + pRigidBody->setKinematicTarget( pose ); } void CPhysicNovodex :: EnableCollision( CBaseEntity *pEntity, int fEnable ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor || pActor->getNbShapes() <= 0 ) + PxActor *pActor = ActorFromEntity( pEntity ); + if( !pActor ) + return; + + PxRigidDynamic *pRigidBody = pActor->is(); + if (pRigidBody->getNbShapes() <= 0) return; if( fEnable ) { - pActor->clearActorFlag( NX_AF_DISABLE_COLLISION ); - pActor->raiseBodyFlag( NX_BF_VISUALIZATION ); + ToggleCollision(pRigidBody, false); + pActor->setActorFlag(PxActorFlag::eVISUALIZATION, true); } else { - pActor->raiseActorFlag( NX_AF_DISABLE_COLLISION ); - pActor->clearBodyFlag( NX_BF_VISUALIZATION ); + ToggleCollision(pRigidBody, true); + pActor->setActorFlag(PxActorFlag::eVISUALIZATION, false); } } void CPhysicNovodex :: MakeKinematic( CBaseEntity *pEntity, int fEnable ) { - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor || pActor->getNbShapes() <= 0 ) + PxActor *pActor = ActorFromEntity( pEntity ); + if (!pActor) return; - if( fEnable ) - pActor->raiseBodyFlag( NX_BF_KINEMATIC ); + PxRigidBody *pRigidBody = pActor->is(); + if (!pRigidBody || pRigidBody->getNbShapes() <= 0) + return; + + if (fEnable) + pRigidBody->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); else - pActor->clearBodyFlag( NX_BF_KINEMATIC ); + pRigidBody->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, false); } void CPhysicNovodex :: SweepTest( CBaseEntity *pTouch, const Vector &start, const Vector &mins, const Vector &maxs, const Vector &end, trace_t *tr ) { - NxActor *pActor = ActorFromEntity( pTouch ); + PxActor *pActor = ActorFromEntity( pTouch ); + if (!pActor) + { + // bad actor? + tr->allsolid = false; + return; + } - if( !pActor || pActor->getNbShapes() <= 0 || pActor->readActorFlag( NX_AF_DISABLE_COLLISION )) + PxRigidBody *pRigidBody = pActor->is(); + if (!pRigidBody || pRigidBody->getNbShapes() <= 0 || !CheckCollision(pRigidBody)) { // bad actor? tr->allsolid = false; @@ -2336,10 +2300,10 @@ void CPhysicNovodex :: SweepTest( CBaseEntity *pTouch, const Vector &start, cons } Vector trace_mins, trace_maxs; - UTIL_MoveBounds( start, mins, maxs, end, trace_mins, trace_maxs ); + UTIL_MoveBounds(start, mins, maxs, end, trace_mins, trace_maxs); // NOTE: pmove code completely ignore a bounds checking. So we need to do it here - if( !BoundsIntersect( trace_mins, trace_maxs, pTouch->pev->absmin, pTouch->pev->absmax )) + if (!BoundsIntersect(trace_mins, trace_maxs, pTouch->pev->absmin, pTouch->pev->absmax)) { tr->allsolid = false; return; @@ -2348,121 +2312,38 @@ void CPhysicNovodex :: SweepTest( CBaseEntity *pTouch, const Vector &start, cons mmesh_t *pMesh; areanode_t *pHeadNode; vec3_t scale = pTouch->pev->startpos.Length() < 0.001f ? vec3_t(1.0f) : pTouch->pev->startpos; - matrix4x4 worldToLocalMat = matrix4x4(pTouch->pev->origin, pTouch->pev->angles, scale).InvertFull(); model_t *mod = (model_t *)MODEL_HANDLE(pTouch->pev->modelindex); if (!pTouch->m_CookedMesh.GetMesh()) { // update cache or build from scratch - NxShape *pShape = pActor->getShapes()[0]; - int shapeType = pShape->getType(); - const NxU32 *indices; - const NxVec3 *verts; Vector triangle[3]; - NxU32 NbTris; - - if( shapeType == NX_SHAPE_CONVEX ) + DecomposedShape shape; + if (!shape.Triangulate(pRigidBody)) { - NxConvexShape *pConvexShape = (NxConvexShape *)pShape; - NxConvexMesh& cm = pConvexShape->getConvexMesh(); - - NbTris = cm.getCount( 0, NX_ARRAY_TRIANGLES ); - indices = (const NxU32 *)cm.getBase( 0, NX_ARRAY_TRIANGLES ); - verts = (const NxVec3 *)cm.getBase( 0, NX_ARRAY_VERTICES ); - } - else if( shapeType == NX_SHAPE_MESH ) - { - NxTriangleMeshShape *pTriangleMeshShape = (NxTriangleMeshShape *)pShape; - NxTriangleMesh& trm = pTriangleMeshShape->getTriangleMesh(); - - NbTris = trm.getCount( 0, NX_ARRAY_TRIANGLES ); - indices = (const NxU32 *)trm.getBase( 0, NX_ARRAY_TRIANGLES ); - verts = (const NxVec3 *)trm.getBase( 0, NX_ARRAY_VERTICES ); - } - else if( shapeType != NX_SHAPE_BOX ) - { - // unsupported mesh type, so skip them + // failed to triangulate, unsupported mesh type, so skip them tr->allsolid = false; return; } - if( shapeType == NX_SHAPE_BOX ) - { - NxVec3 points[8]; - NxVec3 ext, cnt; - NxBounds3 bounds; - NxBox obb; - - // each box shape contain 12 triangles - pTouch->m_CookedMesh.SetDebugName(pTouch->GetModel()); - pTouch->m_CookedMesh.InitMeshBuild(pActor->getNbShapes() * 12); - - for( uint i = 0; i < pActor->getNbShapes(); i++ ) - { - NxBoxShape *pBoxShape = (NxBoxShape *)pActor->getShapes()[i]; - NxMat33 absRot = pBoxShape->getGlobalOrientation(); - NxVec3 absPos = pBoxShape->getGlobalPosition(); + pTouch->m_CookedMesh.SetDebugName(pTouch->GetModel()); + pTouch->m_CookedMesh.InitMeshBuild(shape.GetTrianglesCount()); - // don't use pBoxShape->getWorldAABB it's caused to broke suspension and deadlocks !!! - pBoxShape->getWorldBounds( bounds ); - bounds.getExtents( ext ); - bounds.getCenter( cnt ); - obb = NxBox( cnt, ext, absRot ); - - indices = (const NxU32 *)m_pUtils->NxGetBoxTriangles(); - m_pUtils->NxComputeBoxPoints( obb, points ); - verts = (const NxVec3 *)points; - - for( int j = 0; j < 12; j++ ) - { - NxU32 i0 = *indices++; - NxU32 i1 = *indices++; - NxU32 i2 = *indices++; - triangle[0] = verts[i0]; - triangle[1] = verts[i1]; - triangle[2] = verts[i2]; - - // transform from world to model space - triangle[0] = worldToLocalMat.VectorTransform(triangle[0]); - triangle[1] = worldToLocalMat.VectorTransform(triangle[1]); - triangle[2] = worldToLocalMat.VectorTransform(triangle[2]); - pTouch->m_CookedMesh.AddMeshTrinagle( triangle ); - } - } - } - else + // FIXME: store all meshes as local and use capsule instead of bbox + const auto &indexBuffer = shape.GetIndexBuffer(); + const auto &vertexBuffer = shape.GetVertexBuffer(); + for (size_t i = 0; i < shape.GetTrianglesCount(); i++) { - NxMat33 absRot = pShape->getGlobalOrientation(); - NxVec3 absPos = pShape->getGlobalPosition(); - - pTouch->m_CookedMesh.SetDebugName(pTouch->GetModel()); - pTouch->m_CookedMesh.InitMeshBuild(NbTris); - - // NOTE: we compute triangles in abs coords because player AABB - // can't be transformed as done for not axial cases - // FIXME: store all meshes as local and use capsule instead of bbox - while( NbTris-- ) - { - NxU32 i0 = *indices++; - NxU32 i1 = *indices++; - NxU32 i2 = *indices++; - NxVec3 v0 = verts[i0]; - NxVec3 v1 = verts[i1]; - NxVec3 v2 = verts[i2]; - - absRot.multiply( v0, v0 ); - absRot.multiply( v1, v1 ); - absRot.multiply( v2, v2 ); - triangle[0] = v0 + absPos; - triangle[1] = v1 + absPos; - triangle[2] = v2 + absPos; - - // transform from world to model space - triangle[0] = worldToLocalMat.VectorTransform(triangle[0]); - triangle[1] = worldToLocalMat.VectorTransform(triangle[1]); - triangle[2] = worldToLocalMat.VectorTransform(triangle[2]); - pTouch->m_CookedMesh.AddMeshTrinagle( triangle ); - } + uint32_t i0 = indexBuffer[3 * i]; + uint32_t i1 = indexBuffer[3 * i + 1]; + uint32_t i2 = indexBuffer[3 * i + 2]; + vec3_t v0 = vertexBuffer[i0]; + vec3_t v1 = vertexBuffer[i1]; + vec3_t v2 = vertexBuffer[i2]; + triangle[0] = v0; + triangle[1] = v1; + triangle[2] = v2; + pTouch->m_CookedMesh.AddMeshTrinagle(triangle); } if( !pTouch->m_CookedMesh.FinishMeshBuild( )) @@ -2517,127 +2398,47 @@ void CPhysicNovodex :: SweepEntity( CBaseEntity *pEntity, const Vector &start, c tr->flFraction = 1.0f; tr->vecEndPos = end; - NxActor *pActor = ActorFromEntity( pEntity ); - if( !pActor || pActor->getNbShapes() <= 0 || pEntity->pev->solid == SOLID_NOT ) - return; // only dynamic solid objects can be traced - - NxBox testBox; + PxActor *pActor = ActorFromEntity( pEntity ); + if (!pActor) + return; - // setup trace box - testBox.center = pEntity->Center(); - testBox.rot = pActor->getGlobalOrientation(); - testBox.extents = (pEntity->pev->size * 0.5f); + PxRigidBody *pRigidBody = pActor->is(); + if (!pRigidBody || pRigidBody->getNbShapes() <= 0 || pEntity->pev->solid == SOLID_NOT) + return; // only dynamic solid objects can be traced // test for stuck entity into another - if( start == end ) + if (start == end) { - // update cache or build from scratch - NxShape *pShape = pActor->getShapes()[0]; - int shapeType = pShape->getType(); Vector triangle[3], dirs[3]; - const NxU32 *indices; - const NxVec3 *verts; - NxU32 NbTris; - - if( shapeType == NX_SHAPE_CONVEX ) - { - NxConvexShape *pConvexShape = (NxConvexShape *)pShape; - NxConvexMesh& cm = pConvexShape->getConvexMesh(); - - NbTris = cm.getCount( 0, NX_ARRAY_TRIANGLES ); - indices = (const NxU32 *)cm.getBase( 0, NX_ARRAY_TRIANGLES ); - verts = (const NxVec3 *)cm.getBase( 0, NX_ARRAY_VERTICES ); - } - else if( shapeType == NX_SHAPE_MESH ) - { - NxTriangleMeshShape *pTriangleMeshShape = (NxTriangleMeshShape *)pShape; - NxTriangleMesh& trm = pTriangleMeshShape->getTriangleMesh(); - - NbTris = trm.getCount( 0, NX_ARRAY_TRIANGLES ); - indices = (const NxU32 *)trm.getBase( 0, NX_ARRAY_TRIANGLES ); - verts = (const NxVec3 *)trm.getBase( 0, NX_ARRAY_VERTICES ); + PxTransform globalPose = pRigidBody->getGlobalPose(); + DecomposedShape shape; + if (!shape.Triangulate(pRigidBody)) { + return; // failed to triangulate } - else if( shapeType != NX_SHAPE_BOX ) - { - // unsupported mesh type, so skip them - return; - } - - if( shapeType == NX_SHAPE_BOX ) - { - NxVec3 points[8]; - NxVec3 ext, cnt; - NxBounds3 bounds; - NxBox obb; - - for( uint i = 0; i < pActor->getNbShapes(); i++ ) - { - NxBoxShape *pBoxShape = (NxBoxShape *)pActor->getShapes()[i]; - NxMat33 absRot = pBoxShape->getGlobalOrientation(); - NxVec3 absPos = pBoxShape->getGlobalPosition(); - - // don't use pBoxShape->getWorldAABB it's caused to broke suspension and deadlocks !!! - pBoxShape->getWorldBounds( bounds ); - bounds.getExtents( ext ); - bounds.getCenter( cnt ); - obb = NxBox( cnt, ext, absRot ); - indices = (const NxU32 *)m_pUtils->NxGetBoxTriangles(); - m_pUtils->NxComputeBoxPoints( obb, points ); - verts = (const NxVec3 *)points; - - for( int j = 0; j < 12; j++ ) - { - NxU32 i0 = *indices++; - NxU32 i1 = *indices++; - NxU32 i2 = *indices++; - triangle[0] = verts[i0]; - triangle[1] = verts[i1]; - triangle[2] = verts[i2]; - - for( int k = 0; k < 3; k++ ) - { - dirs[k] = absPos - triangle[k]; - triangle[k] += dirs[k] * -2.0f; - - UTIL_TraceLine( triangle[k], triangle[k], ignore_monsters, pEntity->edict(), tr ); - if( tr->fStartSolid ) return; // one of points in solid - } - } - } - } - else + const auto &indexBuffer = shape.GetIndexBuffer(); + const auto &vertexBuffer = shape.GetVertexBuffer(); + for (size_t i = 0; i < shape.GetTrianglesCount(); i++) { - NxMat33 absRot = pShape->getGlobalOrientation(); - NxVec3 absPos = pShape->getGlobalPosition(); - - // NOTE: we compute triangles in abs coords because player AABB - // can't be transformed as done for not axial cases - // FIXME: store all meshes as local and use capsule instead of bbox - while( NbTris-- ) + uint32_t i0 = indexBuffer[3 * i]; + uint32_t i1 = indexBuffer[3 * i + 1]; + uint32_t i2 = indexBuffer[3 * i + 2]; + vec3_t v0 = vertexBuffer[i0]; + vec3_t v1 = vertexBuffer[i1]; + vec3_t v2 = vertexBuffer[i2]; + + // transform triangles from local to world space + triangle[0] = globalPose.transform(v0); + triangle[1] = globalPose.transform(v1); + triangle[2] = globalPose.transform(v2); + + for (size_t j = 0; j < 3; j++) { - NxU32 i0 = *indices++; - NxU32 i1 = *indices++; - NxU32 i2 = *indices++; - NxVec3 v0 = verts[i0]; - NxVec3 v1 = verts[i1]; - NxVec3 v2 = verts[i2]; - - absRot.multiply( v0, v0 ); - absRot.multiply( v1, v1 ); - absRot.multiply( v2, v2 ); - triangle[0] = v0 + absPos; - triangle[1] = v1 + absPos; - triangle[2] = v2 + absPos; - - for( int i = 0; i < 3; i++ ) - { - dirs[i] = absPos - triangle[i]; - triangle[i] += dirs[i] * -2.0f; - - UTIL_TraceLine( triangle[i], triangle[i], ignore_monsters, pEntity->edict(), tr ); - if( tr->fStartSolid ) return; // one of points in solid - } + dirs[j] = globalPose.p - triangle[j]; + triangle[j] += dirs[j] * -2.0f; + UTIL_TraceLine(triangle[j], triangle[j], ignore_monsters, pEntity->edict(), tr); + if (tr->fStartSolid) + return; // one of points in solid } } return; @@ -2647,25 +2448,37 @@ void CPhysicNovodex :: SweepEntity( CBaseEntity *pEntity, const Vector &start, c Vector vecDir = end - start; float flLength = vecDir.Length(); vecDir = vecDir.Normalize(); - testBox.extents = (pEntity->pev->size * 0.5f); - NxSweepQueryHit result; + + // setup trace box + PxBoxGeometry testBox; + PxTransform initialPose = pRigidBody->getGlobalPose(); + initialPose.p = pEntity->Center(); // does we really need to do this? + testBox.halfExtents = pEntity->pev->size * 0.5f; // make a linear sweep through the world - pActor->raiseActorFlag( NX_AF_DISABLE_COLLISION ); - int numHits = m_pScene->linearOBBSweep( testBox, vecDir * flLength, NX_SF_STATICS|NX_SF_DYNAMICS, NULL, 1, &result, NULL ); - pActor->clearActorFlag( NX_AF_DISABLE_COLLISION ); - if( !numHits || !result.hitShape || result.t > flLength ) + PxSweepBuffer sweepResult; + ToggleCollision(pRigidBody, false); + bool hitOccured = m_pScene->sweep(testBox, initialPose, vecDir, flLength, sweepResult, PxHitFlag::eNORMAL); + ToggleCollision(pRigidBody, true); + + if (!hitOccured || !sweepResult.getNbTouches()) return; // no intersection + const PxSweepHit &hit = sweepResult.getTouch(0); + if (hit.distance > flLength || !hit.actor) + return; // hit missed + // compute fraction - tr->flFraction = (result.t / flLength); + tr->flFraction = (hit.distance / flLength); tr->flFraction = bound( 0.0f, tr->flFraction, 1.0f ); VectorLerp( start, tr->flFraction, end, tr->vecEndPos ); - CBaseEntity *pHit = EntityFromActor( &result.hitShape->getActor( )); - if( pHit ) tr->pHit = pHit->edict(); + CBaseEntity *pHit = EntityFromActor( hit.actor ); + if (pHit) { + tr->pHit = pHit->edict(); + } - tr->vecPlaneNormal = result.normal; + tr->vecPlaneNormal = hit.normal; tr->flPlaneDist = DotProduct( tr->vecEndPos, tr->vecPlaneNormal ); float flDot = DotProduct( vecDir, tr->vecPlaneNormal ); float moveDot = Q_round( flDot, 0.1f ); @@ -2675,4 +2488,4 @@ void CPhysicNovodex :: SweepEntity( CBaseEntity *pEntity, const Vector &start, c tr->fAllSolid = true; } -#endif//USE_PHYSICS_ENGINE +#endif // USE_PHYSICS_ENGINE diff --git a/server/novodex.h b/server/novodex.h index 5ba3c289..25b7da30 100644 --- a/server/novodex.h +++ b/server/novodex.h @@ -1,6 +1,7 @@ /* -novodex.h - this file is a part of Novodex physics engine implementation +novodex.h - part of PhysX physics engine implementation Copyright (C) 2012 Uncle Mike +Copyright (C) 2022 SNMetamorph 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 @@ -12,125 +13,126 @@ 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. */ +#pragma once +#include "physic.h" +#include "extdll.h" +#include "util.h" +#include "cbase.h" -#include "NxPhysicsSDK.h" -#include "NxUserContactReport.h" -#include "NxUserOutputStream.h" -#include "NxScene.h" -#include "NxActor.h" -#include "NxUserStream.h" -#include "NxErrorStream.h" -#include "NxTriangleMeshDesc.h" -#include "NxTriangleMeshShapeDesc.h" -#include "NxForceFieldLinearKernel.h" -#include "NxBoxForceFieldShapeDesc.h" -#include "NxForceFieldDesc.h" -#include "NxDebugRenderable.h" -#include "NxConvexShapeDesc.h" -#include "NxConvexMeshDesc.h" -#include "NxBoxShapeDesc.h" -#include "NxBoxShape.h" -#include "NxMaterial.h" -#include "NxActorDesc.h" -#include "NxCooking.h" -#include "NxTriangle.h" -#include "PhysXLoader.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define DENSITY_FACTOR 0.0013f #define PADDING_FACTOR 0.49f #define SOLVER_ITERATION_COUNT 16 -#define CONVEYOR_SCALE_FACTOR ((1.0f / gpGlobals->frametime) * 0.5f) class CPhysicNovodex : public IPhysicLayer { -private: - NxPhysicsSDK *m_pPhysics; // pointer to the novodex engine - NxScene *m_pScene; // pointer to world scene - model_t *m_pWorldModel; // pointer to worldmodel - - char m_szMapName[256]; - BOOL m_fLoaded; // collision tree is loaded and actual - BOOL m_fDisableWarning; // some warnings will be swallowed - BOOL m_fWorldChanged; // world is changed refresh the statics in case their scale was changed too - BOOL m_fNeedFetchResults; - - NxTriangleMesh *m_pSceneMesh; - NxActor *m_pSceneActor; // scene with installed shape - NxBounds3 worldBounds; - - char p_speeds_msg[1024]; // debug message - - NxErrorStream m_ErrorStream; - NxCookingInterface *m_pCooking; - NxUtilLib *m_pUtils; - - cvar_t *fps_max; public: - void InitPhysic( void ); - void FreePhysic( void ); - void *GetUtilLibrary( void ); - void Update( float flTime ); - void EndFrame( void ); - void RemoveBody( edict_t *pEdict ); - void *CreateBodyFromEntity( CBaseEntity *pEntity ); - void *CreateBoxFromEntity( CBaseEntity *pObject ); - void *CreateKinematicBodyFromEntity( CBaseEntity *pEntity ); - void *CreateStaticBodyFromEntity( CBaseEntity *pObject ); - void *CreateVehicle( CBaseEntity *pObject, string_t scriptName = 0 ); - void *RestoreBody( CBaseEntity *pEntity ); - void SaveBody( CBaseEntity *pObject ); - bool Initialized( void ) { return (m_pPhysics != NULL); } - void SetOrigin( CBaseEntity *pEntity, const Vector &origin ); - void SetAngles( CBaseEntity *pEntity, const Vector &angles ); - void SetVelocity( CBaseEntity *pEntity, const Vector &velocity ); - void SetAvelocity( CBaseEntity *pEntity, const Vector &velocity ); - void MoveObject( CBaseEntity *pEntity, const Vector &finalPos ); - void RotateObject( CBaseEntity *pEntity, const Vector &finalAngle ); - void SetLinearMomentum( CBaseEntity *pEntity, const Vector &velocity ); - void AddImpulse( CBaseEntity *pEntity, const Vector &impulse, const Vector &position, float factor ); - void AddForce( CBaseEntity *pEntity, const Vector &force ); - void EnableCollision( CBaseEntity *pEntity, int fEnable ); - void MakeKinematic( CBaseEntity *pEntity, int fEnable ); - void UpdateVehicle( CBaseEntity *pObject ); + void InitPhysic( void ); + void FreePhysic( void ); + void Update( float flTime ); + void EndFrame( void ); + void RemoveBody( edict_t *pEdict ); + void *CreateBodyFromEntity( CBaseEntity *pEntity ); + void *CreateBoxFromEntity( CBaseEntity *pObject ); + void *CreateKinematicBodyFromEntity( CBaseEntity *pEntity ); + void *CreateStaticBodyFromEntity( CBaseEntity *pObject ); + void *CreateVehicle( CBaseEntity *pObject, string_t scriptName = 0 ); + void *RestoreBody( CBaseEntity *pEntity ); + void SaveBody( CBaseEntity *pObject ); + bool Initialized( void ) { return (m_pPhysics != NULL); } + void SetOrigin( CBaseEntity *pEntity, const Vector &origin ); + void SetAngles( CBaseEntity *pEntity, const Vector &angles ); + void SetVelocity( CBaseEntity *pEntity, const Vector &velocity ); + void SetAvelocity( CBaseEntity *pEntity, const Vector &velocity ); + void MoveObject( CBaseEntity *pEntity, const Vector &finalPos ); + void RotateObject( CBaseEntity *pEntity, const Vector &finalAngle ); + void SetLinearMomentum( CBaseEntity *pEntity, const Vector &velocity ); + void AddImpulse( CBaseEntity *pEntity, const Vector &impulse, const Vector &position, float factor ); + void AddForce( CBaseEntity *pEntity, const Vector &force ); + void EnableCollision( CBaseEntity *pEntity, int fEnable ); + void MakeKinematic( CBaseEntity *pEntity, int fEnable ); + void UpdateVehicle( CBaseEntity *pObject ); int FLoadTree( char *szMapName ); int CheckBINFile( char *szMapName ); int BuildCollisionTree( char *szMapName ); - bool UpdateEntityPos( CBaseEntity *pEntity ); - void UpdateEntityAABB( CBaseEntity *pEntity ); - bool UpdateActorPos( CBaseEntity *pEntity ); - void SetupWorld( void ); - void DebugDraw( void ); - void DrawPSpeeds( void ); - void FreeAllBodies( void ); + bool UpdateEntityPos( CBaseEntity *pEntity ); + void UpdateEntityAABB( CBaseEntity *pEntity ); + bool UpdateActorPos( CBaseEntity *pEntity ); + void SetupWorld( void ); + void DebugDraw( void ); + void DrawPSpeeds( void ); + void FreeAllBodies( void ); + + void TeleportCharacter( CBaseEntity *pEntity ); + void TeleportActor( CBaseEntity *pEntity ); + void MoveCharacter( CBaseEntity *pEntity ); + void MoveKinematic( CBaseEntity *pEntity ); + void SweepTest( CBaseEntity *pTouch, const Vector &start, const Vector &mins, const Vector &maxs, const Vector &end, struct trace_s *tr ); + void SweepEntity( CBaseEntity *pEntity, const Vector &start, const Vector &end, struct gametrace_s *tr ); + bool IsBodySleeping( CBaseEntity *pEntity ); + void *GetCookingInterface( void ) { return m_pCooking; } + void *GetPhysicInterface( void ) { return m_pPhysics; } - void TeleportCharacter( CBaseEntity *pEntity ); - void TeleportActor( CBaseEntity *pEntity ); - void MoveCharacter( CBaseEntity *pEntity ); - void MoveKinematic( CBaseEntity *pEntity ); - void SweepTest( CBaseEntity *pTouch, const Vector &start, const Vector &mins, const Vector &maxs, const Vector &end, struct trace_s *tr ); - void SweepEntity( CBaseEntity *pEntity, const Vector &start, const Vector &end, struct gametrace_s *tr ); - bool IsBodySleeping( CBaseEntity *pEntity ); - void *GetCookingInterface( void ) { return m_pCooking; } - void *GetPhysicInterface( void ) { return m_pPhysics; } private: + class ContactReport; + class DebugRenderer; + class DecomposedShape; + // misc routines - int ConvertEdgeToIndex( model_t *model, int edge ); - NxConvexMesh *ConvexMeshFromBmodel( entvars_t *pev, int modelindex ); - NxConvexMesh *ConvexMeshFromStudio( entvars_t *pev, int modelindex ); - NxConvexMesh *ConvexMeshFromEntity( CBaseEntity *pObject ); - NxTriangleMesh *TriangleMeshFromBmodel( entvars_t *pev, int modelindex ); - NxTriangleMesh *TriangleMeshFromStudio( entvars_t *pev, int modelindex ); - NxTriangleMesh *TriangleMeshFromEntity( CBaseEntity *pObject ); - NxActor *ActorFromEntity( CBaseEntity *pObject ); - CBaseEntity *EntityFromActor( NxActor *pObject ); - void *CreateForceField( CBaseEntity *pEntity, const Vector &force ); + int ConvertEdgeToIndex( model_t *model, int edge ); + physx::PxConvexMesh *ConvexMeshFromBmodel( entvars_t *pev, int modelindex ); + physx::PxConvexMesh *ConvexMeshFromStudio( entvars_t *pev, int modelindex ); + physx::PxConvexMesh *ConvexMeshFromEntity( CBaseEntity *pObject ); + physx::PxTriangleMesh *TriangleMeshFromBmodel( entvars_t *pev, int modelindex ); + physx::PxTriangleMesh *TriangleMeshFromStudio( entvars_t *pev, int modelindex ); + physx::PxTriangleMesh *TriangleMeshFromEntity( CBaseEntity *pObject ); + physx::PxActor *ActorFromEntity( CBaseEntity *pObject ); + CBaseEntity *EntityFromActor( physx::PxActor *pObject ); + bool CheckCollision(physx::PxRigidBody *pActor); + void ToggleCollision(physx::PxRigidBody *pActor, bool enabled); + int CheckFileTimes( const char *szFile1, const char *szFile2 ); + void HullNameForModel( const char *model, char *hullfile, size_t size ); + void MeshNameForModel( const char *model, char *hullfile, size_t size ); + void StudioCalcBoneQuaterion( mstudiobone_t *pbone, mstudioanim_t *panim, Vector4D &q ); + void StudioCalcBonePosition( mstudiobone_t *pbone, mstudioanim_t *panim, Vector &pos ); + bool P_SpeedsMessage( char *out, size_t size ); + +private: + physx::PxPhysics *m_pPhysics; // pointer to the PhysX engine + physx::PxFoundation *m_pFoundation; + physx::PxDefaultCpuDispatcher *m_pDispatcher; + physx::PxScene *m_pScene; // pointer to world scene + model_t *m_pWorldModel; // pointer to worldmodel - int CheckFileTimes( const char *szFile1, const char *szFile2 ); - void HullNameForModel( const char *model, char *hullfile, size_t size ); - void MeshNameForModel( const char *model, char *hullfile, size_t size ); + char m_szMapName[256]; + bool m_fLoaded; // collision tree is loaded and actual + bool m_fDisableWarning; // some warnings will be swallowed + bool m_fWorldChanged; // world is changed refresh the statics in case their scale was changed too + bool m_fNeedFetchResults; - void StudioCalcBoneQuaterion( mstudiobone_t *pbone, mstudioanim_t *panim, Vector4D &q ); - void StudioCalcBonePosition( mstudiobone_t *pbone, mstudioanim_t *panim, Vector &pos ); + physx::PxTriangleMesh *m_pSceneMesh; + physx::PxActor *m_pSceneActor; // scene with installed shape + physx::PxBounds3 m_worldBounds; + physx::PxMaterial *m_pDefaultMaterial; + physx::PxMaterial *m_pConveyorMaterial; - bool P_SpeedsMessage( char *out, size_t size ); + char p_speeds_msg[1024]; // debug message + + ErrorCallback m_ErrorCallback; + physx::PxCooking *m_pCooking; + physx::PxDefaultAllocator m_Allocator; + physx::PxPvd *m_pVisualDebugger; + + cvar_t *fps_max; }; diff --git a/server/physic.h b/server/physic.h index 756ec178..0b08eec7 100644 --- a/server/physic.h +++ b/server/physic.h @@ -15,8 +15,6 @@ GNU General Public License for more details. #ifndef PHYSIC_H #define PHYSIC_H -//#define USE_PHYSICS_ENGINE - class Vector; class CBaseEntity; @@ -27,7 +25,6 @@ class IPhysicLayer virtual void FreePhysic( void ) = 0; virtual void Update( float flTime ) = 0; virtual void EndFrame( void ) = 0; - virtual void *GetUtilLibrary( void ) = 0; virtual bool Initialized( void ) = 0; virtual void RemoveBody( struct edict_s *pEdict ) = 0; virtual void *CreateBodyFromEntity( CBaseEntity *pEntity ) = 0; @@ -49,9 +46,9 @@ class IPhysicLayer virtual void EnableCollision( CBaseEntity *pEntity, int fEnable ) = 0; virtual void MakeKinematic( CBaseEntity *pEntity, int fEnable ) = 0; virtual void UpdateVehicle( CBaseEntity *pObject ) = 0; - virtual int FLoadTree( char *szMapName ) = 0; - virtual int CheckBINFile( char *szMapName ) = 0; - virtual int BuildCollisionTree( char *szMapName ) = 0; + virtual int FLoadTree( char *szMapName ) = 0; + virtual int CheckBINFile( char *szMapName ) = 0; + virtual int BuildCollisionTree( char *szMapName ) = 0; virtual bool UpdateEntityPos( CBaseEntity *pEntity ) = 0; virtual void UpdateEntityAABB( CBaseEntity *pEntity ) = 0; virtual bool UpdateActorPos( CBaseEntity *pEntity ) = 0; @@ -73,4 +70,4 @@ class IPhysicLayer extern void GameInitNullPhysics( void ); // shutdown simulation for some reasons extern IPhysicLayer *WorldPhysic; -#endif//PHYSIC_H +#endif // PHYSIC_H diff --git a/server/physics/NxErrorStream.cpp b/server/physics/NxErrorStream.cpp new file mode 100644 index 00000000..308dad21 --- /dev/null +++ b/server/physics/NxErrorStream.cpp @@ -0,0 +1,54 @@ +/* +NxErrorStream.cpp - a Novodex physics engine implementation +Copyright (C) 2012 Uncle Mike + +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. +*/ + +#include "NxErrorStream.h" +#include "alert.h" +#include "extdll.h" +#include "util.h" + +using namespace physx; + +void ErrorCallback::reportError(physx::PxErrorCode::Enum code, const char* message, const char* file, int line) +{ + switch (code) + { + case PxErrorCode::eINVALID_PARAMETER: + ALERT(at_error, "invalid parameter: %s\n", message); + break; + case PxErrorCode::eINVALID_OPERATION: + ALERT(at_error, "invalid operation: %s\n", message); + break; + case PxErrorCode::eOUT_OF_MEMORY: + ALERT(at_error, "out of memory: %s\n", message); + break; + case PxErrorCode::eDEBUG_INFO: + ALERT(at_console, "%s\n", message); + break; + case PxErrorCode::eDEBUG_WARNING: + if (!m_fHideWarning) { + ALERT(at_warning, "%s\n", message); + } + break; + case PxErrorCode::ePERF_WARNING: + ALERT(at_warning, "performance warning: %s\n", message); + break; + case PxErrorCode::eABORT: + ALERT(at_error, "abort: %s\n", message); + break; + default: + ALERT(at_error, "unknown error: %s\n", message); + break; + } +} diff --git a/server/physics/NxErrorStream.h b/server/physics/NxErrorStream.h index af2eb645..fec0ea46 100644 --- a/server/physics/NxErrorStream.h +++ b/server/physics/NxErrorStream.h @@ -16,49 +16,16 @@ GNU General Public License for more details. #ifndef NX_ERROR_STREAM_H #define NX_ERROR_STREAM_H -class NxErrorStream : public NxUserOutputStream +#include "PxErrorCallback.h" + +class ErrorCallback : public physx::PxErrorCallback { - bool m_fHideWarning; public: - void reportError( NxErrorCode e, const char *message, const char* file, int line ) - { - switch( e ) - { - case NXE_INVALID_PARAMETER: - ALERT( at_error, "invalid parameter: %s\n", message ); - break; - case NXE_INVALID_OPERATION: - ALERT( at_error, "invalid operation: %s\n", message ); - break; - case NXE_OUT_OF_MEMORY: - ALERT( at_error, "out of memory: %s\n", message ); - break; - case NXE_DB_INFO: - ALERT( at_console, "%s\n", message ); - break; - case NXE_DB_WARNING: - if( !m_fHideWarning ) - ALERT( at_warning, "%s\n", message ); - break; - default: - ALERT( at_error, "unknown error: %s\n", message ); - break; - } - } - - NxAssertResponse reportAssertViolation( const char *message, const char *file, int line ) - { - ALERT( at_aiconsole, "access violation : %s (%s line %d)\n", message, file, line ); - return NX_AR_BREAKPOINT; - } - - void print( const char *message ) - { - ALERT( at_console, "%s\n", message ); - } + virtual void reportError(physx::PxErrorCode::Enum code, const char* message, const char* file, int line);\ + void hideWarning(bool bHide) { m_fHideWarning = bHide; } - // hide warning: "createActor: Dynamic triangle mesh instantiated!" - void hideWarning( bool bHide ) { m_fHideWarning = bHide; } +private: + bool m_fHideWarning = false; }; -#endif//NX_ERROR_STREAM +#endif // NX_ERROR_STREAM diff --git a/server/physics/NxUserStream.cpp b/server/physics/NxUserStream.cpp index 6c4cd797..92108e54 100644 --- a/server/physics/NxUserStream.cpp +++ b/server/physics/NxUserStream.cpp @@ -17,11 +17,12 @@ GNU General Public License for more details. #include "extdll.h" #include "util.h" -#include "Nxf.h" -#include "NxSimpleTypes.h" +//#include "Pxf.h" #include "NxUserStream.h" #include +using namespace physx; + // user stream. constructor UserStream :: UserStream( const char* filename, bool load ) : fp(NULL) { @@ -57,7 +58,7 @@ UserStream :: UserStream( const char* filename, bool load ) : fp(NULL) UserStream :: ~UserStream() { if( load_file ) - { + { FREE_FILE( buffer ); m_iOffset = m_iLength = 0; buffer = NULL; @@ -87,54 +88,21 @@ void UserStream :: CreatePath( char *path ) } // Loading API -NxU8 UserStream :: readByte() const -{ - NxU8 b; - readBuffer( &b, sizeof( NxU8 )); - return b; -} - -NxU16 UserStream :: readWord() const -{ - NxU16 w; - readBuffer( &w, sizeof( NxU16 )); - return w; -} - -NxU32 UserStream :: readDword() const -{ - NxU32 d; - readBuffer( &d, sizeof( NxU32 )); - return d; -} - -float UserStream :: readFloat() const +uint32_t UserStream::read( void *outbuf, uint32_t size ) { - NxReal f; - readBuffer( &f, sizeof( NxReal )); - return f; -} - -double UserStream :: readDouble() const -{ - NxF64 f; - readBuffer( &f, sizeof( NxF64 )); - return f; -} - -void UserStream :: readBuffer( void *outbuf, NxU32 size ) const -{ - if( size == 0 ) return; + if( size == 0 ) + return 0; #ifdef _DEBUG // in case we failed to loading file memset( outbuf, 0x00, size ); #endif - if( !buffer || !outbuf ) return; + if( !buffer || !outbuf ) + return 0; // check for enough room if( m_iOffset >= m_iLength ) - return; // hit EOF + return 0; // hit EOF size_t read_size = 0; @@ -153,105 +121,33 @@ void UserStream :: readBuffer( void *outbuf, NxU32 size ) const ALERT( at_warning, "readBuffer: buffer is overrun\n" ); } - NX_ASSERT(read_size); + return read_size; } // Saving API -NxStream& UserStream :: storeByte( NxU8 b ) -{ - size_t w = fwrite( &b, sizeof( NxU8 ), 1, fp ); - NX_ASSERT( w ); - - return *this; -} - -NxStream& UserStream :: storeWord( NxU16 w ) -{ - size_t ww = fwrite( &w, sizeof( NxU16 ), 1, fp ); - NX_ASSERT( ww ); - return *this; -} - -NxStream& UserStream :: storeDword( NxU32 d ) -{ - size_t w = fwrite( &d, sizeof( NxU32 ), 1, fp ); - NX_ASSERT( w ); - - return *this; -} - -NxStream& UserStream :: storeFloat( NxReal f ) -{ - size_t w = fwrite( &f, sizeof( NxReal ), 1, fp ); - NX_ASSERT( w ); - - return *this; -} - -NxStream& UserStream :: storeDouble( NxF64 f ) -{ - size_t w = fwrite( &f, sizeof( NxF64 ), 1, fp ); - NX_ASSERT( w ); - - return *this; -} - -NxStream& UserStream :: storeBuffer( const void *buffer, NxU32 size ) +uint32_t UserStream::write( const void *buffer, uint32_t size ) { size_t w = fwrite( buffer, size, 1, fp ); - NX_ASSERT( w ); - - return *this; + return w; } -MemoryWriteBuffer :: MemoryWriteBuffer() : currentSize(0), maxSize(0), data(NULL) +MemoryWriteBuffer::MemoryWriteBuffer() : currentSize(0), maxSize(0), data(NULL) { } -MemoryWriteBuffer :: ~MemoryWriteBuffer() +MemoryWriteBuffer::~MemoryWriteBuffer() { free( data ); } -NxStream& MemoryWriteBuffer :: storeByte (NxU8 b ) -{ - storeBuffer( &b, sizeof( NxU8 )); - return *this; -} - -NxStream& MemoryWriteBuffer :: storeWord( NxU16 w ) -{ - storeBuffer( &w, sizeof( NxU16 )); - return *this; -} - -NxStream& MemoryWriteBuffer :: storeDword( NxU32 d ) -{ - storeBuffer( &d, sizeof( NxU32 )); - return *this; -} - -NxStream& MemoryWriteBuffer :: storeFloat( NxReal f ) -{ - storeBuffer( &f, sizeof( NxReal )); - return *this; -} - -NxStream& MemoryWriteBuffer :: storeDouble( NxF64 f ) -{ - storeBuffer( &f, sizeof( NxF64 )); - return *this; -} - -NxStream& MemoryWriteBuffer :: storeBuffer( const void *buffer, NxU32 size ) +uint32_t MemoryWriteBuffer::write( const void *buffer, uint32_t size ) { - NxU32 expectedSize = currentSize + size; - + PxU32 expectedSize = currentSize + size; if( expectedSize > maxSize ) { maxSize = expectedSize + 4096; - NxU8 *newData = (NxU8 *)malloc( maxSize ); + PxU8 *newData = (PxU8 *)malloc( maxSize ); if( data ) { memcpy( newData, data, currentSize ); @@ -262,10 +158,10 @@ NxStream& MemoryWriteBuffer :: storeBuffer( const void *buffer, NxU32 size ) memcpy( data + currentSize, buffer, size ); currentSize += size; - return *this; + return size; } -MemoryReadBuffer :: MemoryReadBuffer( const NxU8 *data ) : buffer(data) +MemoryReadBuffer::MemoryReadBuffer(const PxU8* data) : buffer(data) { } @@ -274,50 +170,11 @@ MemoryReadBuffer::~MemoryReadBuffer() // We don't own the data => no delete } -NxU8 MemoryReadBuffer :: readByte() const -{ - NxU8 b; - memcpy( &b, buffer, sizeof( NxU8 )); - buffer += sizeof( NxU8 ); - return b; -} - -NxU16 MemoryReadBuffer :: readWord() const -{ - NxU16 w; - memcpy( &w, buffer, sizeof( NxU16 )); - buffer += sizeof( NxU16 ); - return w; -} - -NxU32 MemoryReadBuffer :: readDword() const -{ - NxU32 d; - memcpy( &d, buffer, sizeof( NxU32 )); - buffer += sizeof( NxU32 ); - return d; -} - -float MemoryReadBuffer :: readFloat() const -{ - float f; - memcpy( &f, buffer, sizeof( float )); - buffer += sizeof( float ); - return f; -} - -double MemoryReadBuffer :: readDouble() const -{ - double f; - memcpy( &f, buffer, sizeof( double )); - buffer += sizeof( double ); - return f; -} - -void MemoryReadBuffer :: readBuffer( void *dest, NxU32 size ) const +uint32_t MemoryReadBuffer::read(void *dest, uint32_t count) { - memcpy( dest, buffer, size ); - buffer += size; + memcpy(dest, buffer, count); + buffer += count; + return count; } -#endif // USE_PHYSICS_ENGINE \ No newline at end of file +#endif // USE_PHYSICS_ENGINE diff --git a/server/physics/NxUserStream.h b/server/physics/NxUserStream.h index 24a9ad0f..58614bf0 100644 --- a/server/physics/NxUserStream.h +++ b/server/physics/NxUserStream.h @@ -16,27 +16,18 @@ GNU General Public License for more details. #ifndef NX_USER_STREAM #define NX_USER_STREAM -#include "NxStream.h" +#include "PxIO.h" +#include "PxSimpleTypes.h" -class UserStream : public NxStream +class UserStream : public physx::PxInputStream, public physx::PxOutputStream { public: UserStream( const char *filename, bool load ); virtual ~UserStream(); - virtual NxU8 readByte() const; - virtual NxU16 readWord() const; - virtual NxU32 readDword() const; - virtual float readFloat() const; - virtual double readDouble() const; - virtual void readBuffer( void *outbuf, NxU32 size ) const; + virtual uint32_t read( void *outbuf, uint32_t size ); + virtual uint32_t write( const void *buffer, uint32_t size ); - virtual NxStream& storeByte( NxU8 b ); - virtual NxStream& storeWord( NxU16 w ); - virtual NxStream& storeDword( NxU32 d ); - virtual NxStream& storeFloat( NxReal f ); - virtual NxStream& storeDouble( NxF64 f ); - virtual NxStream& storeBuffer( const void* buffer, NxU32 size ); private: bool load_file; // state FILE *fp; @@ -48,52 +39,29 @@ class UserStream : public NxStream void CreatePath( char *path ); }; -class MemoryWriteBuffer : public NxStream +class MemoryWriteBuffer : public physx::PxOutputStream { public: MemoryWriteBuffer(); virtual ~MemoryWriteBuffer(); - virtual NxU8 readByte() const { NX_ASSERT(0); return 0; } - virtual NxU16 readWord() const { NX_ASSERT(0); return 0; } - virtual NxU32 readDword() const { NX_ASSERT(0); return 0; } - virtual float readFloat() const { NX_ASSERT(0); return 0.0f; } - virtual double readDouble() const { NX_ASSERT(0); return 0.0; } - virtual void readBuffer( void *buffer, NxU32 size ) const { NX_ASSERT(0); } + virtual uint32_t write(const void *src, uint32_t count); - virtual NxStream& storeByte( NxU8 b ); - virtual NxStream& storeWord( NxU16 w ); - virtual NxStream& storeDword( NxU32 d ); - virtual NxStream& storeFloat( NxReal f ); - virtual NxStream& storeDouble( NxF64 f ); - virtual NxStream& storeBuffer( const void* buffer, NxU32 size ); - - NxU32 currentSize; - NxU32 maxSize; - NxU8 *data; + physx::PxU32 currentSize; + physx::PxU32 maxSize; + physx::PxU8 *data; }; -class MemoryReadBuffer : public NxStream +class MemoryReadBuffer : public physx::PxInputStream { public: - MemoryReadBuffer( const NxU8* data ); + MemoryReadBuffer(const physx::PxU8 *data); virtual ~MemoryReadBuffer(); - virtual NxU8 readByte() const; - virtual NxU16 readWord() const; - virtual NxU32 readDword() const; - virtual float readFloat() const; - virtual double readDouble() const; - virtual void readBuffer( void* buffer, NxU32 size ) const; - - virtual NxStream& storeByte( NxU8 b ) { NX_ASSERT(0); return *this; } - virtual NxStream& storeWord( NxU16 w ) { NX_ASSERT(0); return *this; } - virtual NxStream& storeDword( NxU32 d ) { NX_ASSERT(0); return *this; } - virtual NxStream& storeFloat( NxReal f ) { NX_ASSERT(0); return *this; } - virtual NxStream& storeDouble( NxF64 f ) { NX_ASSERT(0); return *this; } - virtual NxStream& storeBuffer( const void *buffer, NxU32 size ) { NX_ASSERT(0); return *this; } + virtual uint32_t read(void *dest, uint32_t count); - mutable const NxU8* buffer; +private: + mutable const physx::PxU8 *buffer; }; -#endif//NX_USER_STREAM +#endif // NX_USER_STREAM diff --git a/server/physics/contact_report.cpp b/server/physics/contact_report.cpp new file mode 100644 index 00000000..5bc98421 --- /dev/null +++ b/server/physics/contact_report.cpp @@ -0,0 +1,71 @@ +/* +contact_report.h - part of PhysX physics engine implementation +Copyright (C) 2023 SNMetamorph + +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. +*/ +#include "contact_report.h" +#include "extdll.h" +#include "util.h" +#include "cbase.h" + +#define CONVEYOR_SCALE_FACTOR ((1.0f / gpGlobals->frametime) * 0.5f) + +using namespace physx; + +CPhysicNovodex::ContactReport &CPhysicNovodex::ContactReport::getInstance() +{ + static ContactReport instance; + return instance; +} + +void CPhysicNovodex::ContactReport::onContact(const PxContactPairHeader &pairHeader, const PxContactPair *pairs, PxU32 nbPairs) +{ + for (PxU32 i = 0; i < nbPairs; i++) + { + const PxContactPair &pair = pairs[i]; + if (!FBitSet(pair.events, PxPairFlag::eNOTIFY_TOUCH_PERSISTS)) { + return; + } + + edict_t* e1 = (edict_t*)pairHeader.actors[0]->userData; + edict_t* e2 = (edict_t*)pairHeader.actors[1]->userData; + + if (!e1 || !e2) + return; + + if (e1->v.flags & FL_CONVEYOR) + { + PxRigidBody *actor = pairHeader.actors[1]->is(); + Vector basevelocity = e1->v.movedir * e1->v.speed * CONVEYOR_SCALE_FACTOR; + actor->setForceAndTorque(basevelocity, PxVec3(0.f), PxForceMode::eIMPULSE); + } + + if (e2->v.flags & FL_CONVEYOR) + { + PxRigidBody* actor = pairHeader.actors[0]->is(); + Vector basevelocity = e2->v.movedir * e2->v.speed * CONVEYOR_SCALE_FACTOR; + actor->setForceAndTorque(basevelocity, PxVec3(0.f), PxForceMode::eIMPULSE); + } + + if (e1 && e1->v.solid != SOLID_NOT) + { + // FIXME: build trace info + DispatchTouch(e1, e2); + } + + if (e2 && e2->v.solid != SOLID_NOT) + { + // FIXME: build trace info + DispatchTouch(e1, e2); + } + } +} diff --git a/server/physics/contact_report.h b/server/physics/contact_report.h new file mode 100644 index 00000000..4d7aeecc --- /dev/null +++ b/server/physics/contact_report.h @@ -0,0 +1,35 @@ +/* +contact_report.h - part of PhysX physics engine implementation +Copyright (C) 2023 SNMetamorph + +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. +*/ +#pragma once +#include "novodex.h" +#include "PxPhysicsAPI.h" +#include "PxSimulationEventCallback.h" + +class CPhysicNovodex::ContactReport : public physx::PxSimulationEventCallback +{ +public: + static CPhysicNovodex::ContactReport &getInstance(); + virtual void onConstraintBreak(physx::PxConstraintInfo* constraints, physx::PxU32 count) {}; + virtual void onWake(physx::PxActor** actors, physx::PxU32 count) {}; + virtual void onSleep(physx::PxActor** actors, physx::PxU32 count) {}; + virtual void onTrigger(physx::PxTriggerPair* pairs, physx::PxU32 count) {}; + virtual void onAdvance(const physx::PxRigidBody* const* bodyBuffer, const physx::PxTransform* poseBuffer, const physx::PxU32 count) {}; + virtual void onContact(const physx::PxContactPairHeader& pairHeader, const physx::PxContactPair* pairs, physx::PxU32 nbPairs); + +private: + ContactReport() = default; + ContactReport(const ContactReport&) = delete; + ContactReport& operator=(const ContactReport&) = delete; +}; diff --git a/server/physics/debug_renderer.cpp b/server/physics/debug_renderer.cpp new file mode 100644 index 00000000..469b3067 --- /dev/null +++ b/server/physics/debug_renderer.cpp @@ -0,0 +1,84 @@ +/* +debug_renderer.cpp - part of PhysX physics engine implementation +Copyright (C) 2023 SNMetamorph + +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. +*/ +#include "debug_renderer.h" +#include "extdll.h" +#include "triangleapi.h" +#include "util.h" + +using namespace physx; + +CPhysicNovodex::DebugRenderer &CPhysicNovodex::DebugRenderer::GetInstance() +{ + static DebugRenderer instance; + return instance; +} + +void CPhysicNovodex::DebugRenderer::SetupColor(PxU32 color) const +{ + float blue = PxU32(color & 0xff) / 255.0f; + float green = PxU32((color >> 8) & 0xff) / 255.0f; + float red = PxU32((color >> 16) & 0xff) / 255.0f; + float alpha = PxU32((color >> 24) & 0xff) / 255.0f; + Tri->Color4f(red, green, blue, alpha); +} + +void CPhysicNovodex::DebugRenderer::RenderData(const PxRenderBuffer &data) const +{ + RenderPoints(data.getPoints(), data.getNbPoints()); + RenderLines(data.getLines(), data.getNbLines()); + RenderTriangles(data.getTriangles(), data.getNbTriangles()); +} + +void CPhysicNovodex::DebugRenderer::RenderPoints(const PxDebugPoint *points, PxU32 count) const +{ + Tri->Begin(TRI_POINTS); + for (PxU32 i = 0; i < count; i++) + { + const PxDebugPoint &point = points[i]; + SetupColor(point.color); + Tri->Vertex3fv((float *)&point.pos.x); + } + Tri->End(); +} + +void CPhysicNovodex::DebugRenderer::RenderLines(const PxDebugLine *lines, PxU32 count) const +{ + Tri->Begin(TRI_LINES); + for (PxU32 i = 0; i < count; i++) + { + const PxDebugLine &line = lines[i]; + SetupColor(line.color0); + Tri->Vertex3fv((float *)&line.pos0.x); + SetupColor(line.color1); + Tri->Vertex3fv((float *)&line.pos1.x); + } + Tri->End(); +} + +void CPhysicNovodex::DebugRenderer::RenderTriangles(const PxDebugTriangle *triangles, PxU32 count) const +{ + Tri->Begin(TRI_TRIANGLES); + for (PxU32 i = 0; i < count; i++) + { + const PxDebugTriangle &tri = triangles[i]; + SetupColor(tri.color0); + Tri->Vertex3fv((float *)&tri.pos0.x); + SetupColor(tri.color1); + Tri->Vertex3fv((float *)&tri.pos1.x); + SetupColor(tri.color2); + Tri->Vertex3fv((float *)&tri.pos2.x); + } + Tri->End(); +} diff --git a/server/physics/debug_renderer.h b/server/physics/debug_renderer.h new file mode 100644 index 00000000..1a901631 --- /dev/null +++ b/server/physics/debug_renderer.h @@ -0,0 +1,35 @@ +/* +debug_renderer.h - part of PhysX physics engine implementation +Copyright (C) 2023 SNMetamorph + +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. +*/ +#pragma once +#include "novodex.h" +#include "PxPhysicsAPI.h" + +class CPhysicNovodex; +class CPhysicNovodex::DebugRenderer +{ +public: + static DebugRenderer &GetInstance(); + void RenderData(const physx::PxRenderBuffer& data) const; + +private: + DebugRenderer() = default; + DebugRenderer(const DebugRenderer&) = delete; + DebugRenderer& operator=(const DebugRenderer&) = delete; + + void SetupColor(physx::PxU32 color) const; + void RenderPoints(const physx::PxDebugPoint *points, physx::PxU32 count) const; + void RenderLines(const physx::PxDebugLine *lines, physx::PxU32 count) const; + void RenderTriangles(const physx::PxDebugTriangle *triangles, physx::PxU32 count) const; +}; diff --git a/server/physics/triangulated_shape.cpp b/server/physics/triangulated_shape.cpp new file mode 100644 index 00000000..9eec19fb --- /dev/null +++ b/server/physics/triangulated_shape.cpp @@ -0,0 +1,229 @@ +/* +triangulated_shape.cpp - class for decomposing rigid body shapes to triangles +Copyright (C) 2023 SNMetamorph + +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. +*/ +#include "triangulated_shape.h" + +using namespace physx; + +bool CPhysicNovodex::DecomposedShape::Triangulate(PxRigidBody *rigidBody) +{ + PxShape *pShape; + rigidBody->getShapes(&pShape, 1); + PxGeometryType::Enum shapeType = pShape->getGeometryType(); + + if (shapeType == PxGeometryType::eBOX) { + DecomposeBox(rigidBody); + } + else if (shapeType == PxGeometryType::eCONVEXMESH) { + DecomposeConvexMesh(rigidBody, pShape); + } + else if (shapeType == PxGeometryType::eTRIANGLEMESH) { + DecomposeTriangleMesh(rigidBody, pShape); + } + else { + // unsupported mesh type, so skip them + return false; + } + return true; +} + +size_t CPhysicNovodex::DecomposedShape::GetTrianglesCount() const +{ + return m_trianglesCount; +} + +const std::vector& CPhysicNovodex::DecomposedShape::GetIndexBuffer() const +{ + return m_indexBuffer; +} + +const std::vector& CPhysicNovodex::DecomposedShape::GetVertexBuffer() const +{ + return m_vertexBuffer; +} + +void CPhysicNovodex::DecomposedShape::DecomposeBox(physx::PxRigidBody *rigidBody) +{ + PxBoxGeometry box; + constexpr size_t vertsPerShape = 8; + constexpr size_t trianglesCount = 12; + constexpr uint32_t trianglesIndices[trianglesCount][3] = { + { 1, 2, 3 }, + { 3, 0, 1 }, + { 3, 0, 4 }, + { 4, 7, 3 }, + { 2, 3, 7 }, + { 7, 6, 2 }, + { 1, 0, 4 }, + { 4, 5, 1 }, + { 4, 5, 6 }, + { 6, 7, 4 }, + { 1, 5, 6 }, + { 6, 2, 1 } + }; + + for (PxU32 i = 0; i < rigidBody->getNbShapes(); i++) + { + PxShape *pBoxShape; + rigidBody->getShapes(&pBoxShape, 1, i); + if (pBoxShape->getGeometryType() != PxGeometryType::eBOX) { + // warn about unsupported shape + continue; + } + + pBoxShape->getBoxGeometry(box); + m_indexBuffer.reserve(m_indexBuffer.size() + trianglesCount * 3); + m_vertexBuffer.reserve(m_vertexBuffer.size() + vertsPerShape); + + // manually define box corner points + m_vertexBuffer.emplace_back(box.halfExtents.x, -box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, -box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(box.halfExtents.x, box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(box.halfExtents.x, -box.halfExtents.y, box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, -box.halfExtents.y, box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, box.halfExtents.y, box.halfExtents.z); + m_vertexBuffer.emplace_back(box.halfExtents.x, box.halfExtents.y, box.halfExtents.z); + + for (size_t j = 0; j < trianglesCount; j++) + { + m_indexBuffer.push_back(i * (vertsPerShape - 1) + trianglesIndices[j][0]); + m_indexBuffer.push_back(i * (vertsPerShape - 1) + trianglesIndices[j][1]); + m_indexBuffer.push_back(i * (vertsPerShape - 1) + trianglesIndices[j][2]); + m_trianglesCount += 1; + } + } +} + +void CPhysicNovodex::DecomposedShape::DecomposeConvexMesh(physx::PxRigidBody *rigidBody, physx::PxShape *shape) +{ + PxConvexMesh *convexMesh = shape->getGeometry().convexMesh().convexMesh; + PxU32 nbVerts = convexMesh->getNbVertices(); + const PxVec3 *convexVerts = convexMesh->getVertices(); + const PxU8 *indexBuffer = convexMesh->getIndexBuffer(); + + PxU32 totalTriangles = 0; + PxU32 totalVerts = 0; + for (PxU32 i = 0; i < convexMesh->getNbPolygons(); i++) + { + PxHullPolygon face; + bool status = convexMesh->getPolygonData(i, face); + PX_ASSERT(status); + totalVerts += face.mNbVerts; + totalTriangles += face.mNbVerts - 2; + } + + // preallocate memory for things + m_vertexBuffer.resize(totalVerts); + m_indexBuffer.resize(3 * totalTriangles); + m_trianglesCount = totalTriangles; + + PxU32 offset = 0; + PxU32 indexOffset = 0; + for (PxU32 i = 0; i < convexMesh->getNbPolygons(); i++) + { + PxHullPolygon face; + bool status = convexMesh->getPolygonData(i, face); + PX_ASSERT(status); + const PxU8 *faceIndices = indexBuffer + face.mIndexBase; + for (PxU32 j = 0; j < face.mNbVerts; j++) { + m_vertexBuffer[offset + j] = convexVerts[faceIndices[j]]; + } + for (PxU32 j = 2; j < face.mNbVerts; j++) + { + m_indexBuffer[indexOffset++] = offset; + m_indexBuffer[indexOffset++] = offset + j; + m_indexBuffer[indexOffset++] = offset + j - 1; + } + offset += face.mNbVerts; + } +} + +void CPhysicNovodex::DecomposedShape::DecomposeTriangleMesh(physx::PxRigidBody *rigidBody, physx::PxShape *shape) +{ + PxTriangleMesh *mesh = shape->getGeometry().triangleMesh().triangleMesh; + const PxU32 nbVerts = mesh->getNbVertices(); + const PxVec3 *verts = mesh->getVertices(); + const PxU32 nbTris = mesh->getNbTriangles(); + const void *indices = mesh->getTriangles(); + const bool has16bitIndices = mesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES; + + m_vertexBuffer.reserve(nbVerts); + m_indexBuffer.reserve(nbTris * 3); + m_trianglesCount = nbTris; + + for (size_t i = 0; i < nbVerts; i++) { + m_vertexBuffer.push_back(verts[i]); + } + + for (size_t i = 0; i < nbTris; i++) + { + for (size_t j = 0; j < 3; j++) + { + if (has16bitIndices) { + const PxU16* triIndices = reinterpret_cast(indices); + const PxU16 index = triIndices[3 * i + j]; + m_indexBuffer.push_back(static_cast(index)); + } + else { + const PxU32* triIndices = reinterpret_cast(indices); + const PxU32 index = triIndices[3 * i + j]; + m_indexBuffer.push_back(index); + } + } + } +} + +void CPhysicNovodex::DecomposedShape::DebugDummyBox() +{ + PxBoxGeometry box; + constexpr size_t vertsPerShape = 8; + constexpr size_t trianglesCount = 12; + constexpr uint32_t trianglesIndices[trianglesCount][3] = { + { 1, 2, 3 }, + { 3, 0, 1 }, + { 3, 0, 4 }, + { 4, 7, 3 }, + { 2, 3, 7 }, + { 7, 6, 2 }, + { 1, 0, 4 }, + { 4, 5, 1 }, + { 4, 5, 6 }, + { 6, 7, 4 }, + { 1, 5, 6 }, + { 6, 2, 1 } + }; + + box.halfExtents = PxVec3(16.f, 16.f, 16.f); + m_indexBuffer.reserve(m_indexBuffer.size() + trianglesCount * 3); + m_vertexBuffer.reserve(m_vertexBuffer.size() + vertsPerShape); + + // manually define box corner points + m_vertexBuffer.emplace_back(box.halfExtents.x, -box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, -box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(box.halfExtents.x, box.halfExtents.y, -box.halfExtents.z); + m_vertexBuffer.emplace_back(box.halfExtents.x, -box.halfExtents.y, box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, -box.halfExtents.y, box.halfExtents.z); + m_vertexBuffer.emplace_back(-box.halfExtents.x, box.halfExtents.y, box.halfExtents.z); + m_vertexBuffer.emplace_back(box.halfExtents.x, box.halfExtents.y, box.halfExtents.z); + + for (size_t j = 0; j < trianglesCount; j++) + { + m_indexBuffer.push_back(trianglesIndices[j][0]); + m_indexBuffer.push_back(trianglesIndices[j][1]); + m_indexBuffer.push_back(trianglesIndices[j][2]); + m_trianglesCount += 1; + } +} diff --git a/server/physics/triangulated_shape.h b/server/physics/triangulated_shape.h new file mode 100644 index 00000000..434cbd93 --- /dev/null +++ b/server/physics/triangulated_shape.h @@ -0,0 +1,40 @@ +/* +triangulated_shape.h - class for decomposing rigid body shapes to triangles +Copyright (C) 2023 SNMetamorph + +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. +*/ +#pragma once +#include "novodex.h" +#include "PxPhysicsAPI.h" +#include +#include + +class CPhysicNovodex::DecomposedShape +{ +public: + DecomposedShape() = default; + + bool Triangulate(physx::PxRigidBody *rigidBody); + size_t GetTrianglesCount() const; + const std::vector& GetIndexBuffer() const; + const std::vector& GetVertexBuffer() const; + +private: + void DecomposeBox(physx::PxRigidBody *rigidBody); + void DecomposeConvexMesh(physx::PxRigidBody *rigidBody, physx::PxShape *shape); + void DecomposeTriangleMesh(physx::PxRigidBody *rigidBody, physx::PxShape *shape); + void DebugDummyBox(); + + size_t m_trianglesCount = 0; + std::vector m_indexBuffer; + std::vector m_vertexBuffer; +}; diff --git a/server/util.h b/server/util.h index c8d20c17..35383393 100644 --- a/server/util.h +++ b/server/util.h @@ -12,9 +12,7 @@ * without written permission from Valve LLC. * ****/ -// -// Misc utility code -// +#pragma once #include "stringlib.h" #ifndef ACTIVITY_H